﻿/*
* jQuery Mobile v1.4.5
* http://jquerymobile.com
*
* Copyright 2010, 2014 jQuery Foundation, Inc. and other contributors
* Released under the MIT license.
* http://jquery.org/license
*
*/

(function (root, doc, factory) {
    if (typeof define === "function" && define.amd) {
        // AMD. Register as an anonymous module.
        define(["jquery"], function ($) {
            factory($, root, doc);
            return $.mobile;
        });
    } else {
        // Browser globals
        factory(root.jQuery, root, doc);
    }
}(this, document, function (jQuery, window, document, undefined) {/*!
 * jQuery hashchange event - v1.3 - 7/21/2010
 * http://benalman.com/projects/jquery-hashchange-plugin/
 * 
 * Copyright (c) 2010 "Cowboy" Ben Alman
 * Dual licensed under the MIT and GPL licenses.
 * http://benalman.com/about/license/
 */

    // Script: jQuery hashchange event
    //
    // *Version: 1.3, Last updated: 7/21/2010*
    // 
    // Project Home - http://benalman.com/projects/jquery-hashchange-plugin/
    // GitHub       - http://github.com/cowboy/jquery-hashchange/
    // Source       - http://github.com/cowboy/jquery-hashchange/raw/master/jquery.ba-hashchange.js
    // (Minified)   - http://github.com/cowboy/jquery-hashchange/raw/master/jquery.ba-hashchange.min.js (0.8kb gzipped)
    // 
    // About: License
    // 
    // Copyright (c) 2010 "Cowboy" Ben Alman,
    // Dual licensed under the MIT and GPL licenses.
    // http://benalman.com/about/license/
    // 
    // About: Examples
    // 
    // These working examples, complete with fully commented code, illustrate a few
    // ways in which this plugin can be used.
    // 
    // hashchange event - http://benalman.com/code/projects/jquery-hashchange/examples/hashchange/
    // document.domain - http://benalman.com/code/projects/jquery-hashchange/examples/document_domain/
    // 
    // About: Support and Testing
    // 
    // Information about what version or versions of jQuery this plugin has been
    // tested with, what browsers it has been tested in, and where the unit tests
    // reside (so you can test it yourself).
    // 
    // jQuery Versions - 1.2.6, 1.3.2, 1.4.1, 1.4.2
    // Browsers Tested - Internet Explorer 6-8, Firefox 2-4, Chrome 5-6, Safari 3.2-5,
    //                   Opera 9.6-10.60, iPhone 3.1, Android 1.6-2.2, BlackBerry 4.6-5.
    // Unit Tests      - http://benalman.com/code/projects/jquery-hashchange/unit/
    // 
    // About: Known issues
    // 
    // While this jQuery hashchange event implementation is quite stable and
    // robust, there are a few unfortunate browser bugs surrounding expected
    // hashchange event-based behaviors, independent of any JavaScript
    // window.onhashchange abstraction. See the following examples for more
    // information:
    // 
    // Chrome: Back Button - http://benalman.com/code/projects/jquery-hashchange/examples/bug-chrome-back-button/
    // Firefox: Remote XMLHttpRequest - http://benalman.com/code/projects/jquery-hashchange/examples/bug-firefox-remote-xhr/
    // WebKit: Back Button in an Iframe - http://benalman.com/code/projects/jquery-hashchange/examples/bug-webkit-hash-iframe/
    // Safari: Back Button from a different domain - http://benalman.com/code/projects/jquery-hashchange/examples/bug-safari-back-from-diff-domain/
    // 
    // Also note that should a browser natively support the window.onhashchange 
    // event, but not report that it does, the fallback polling loop will be used.
    // 
    // About: Release History
    // 
    // 1.3   - (7/21/2010) Reorganized IE6/7 Iframe code to make it more
    //         "removable" for mobile-only development. Added IE6/7 document.title
    //         support. Attempted to make Iframe as hidden as possible by using
    //         techniques from http://www.paciellogroup.com/blog/?p=604. Added 
    //         support for the "shortcut" format $(window).hashchange( fn ) and
    //         $(window).hashchange() like jQuery provides for built-in events.
    //         Renamed jQuery.hashchangeDelay to <jQuery.fn.hashchange.delay> and
    //         lowered its default value to 50. Added <jQuery.fn.hashchange.domain>
    //         and <jQuery.fn.hashchange.src> properties plus document-domain.html
    //         file to address access denied issues when setting document.domain in
    //         IE6/7.
    // 1.2   - (2/11/2010) Fixed a bug where coming back to a page using this plugin
    //         from a page on another domain would cause an error in Safari 4. Also,
    //         IE6/7 Iframe is now inserted after the body (this actually works),
    //         which prevents the page from scrolling when the event is first bound.
    //         Event can also now be bound before DOM ready, but it won't be usable
    //         before then in IE6/7.
    // 1.1   - (1/21/2010) Incorporated document.documentMode test to fix IE8 bug
    //         where browser version is incorrectly reported as 8.0, despite
    //         inclusion of the X-UA-Compatible IE=EmulateIE7 meta tag.
    // 1.0   - (1/9/2010) Initial Release. Broke out the jQuery BBQ event.special
    //         window.onhashchange functionality into a separate plugin for users
    //         who want just the basic event & back button support, without all the
    //         extra awesomeness that BBQ provides. This plugin will be included as
    //         part of jQuery BBQ, but also be available separately.

    (function ($, window, undefined) {
        '$:nomunge'; // Used by YUI compressor.

        // Reused string.
        var str_hashchange = 'hashchange',

          // Method / object references.
          doc = document,
          fake_onhashchange,
          special = $.event.special,

          // Does the browser support window.onhashchange? Note that IE8 running in
          // IE7 compatibility mode reports true for 'onhashchange' in window, even
          // though the event isn't supported, so also test document.documentMode.
          doc_mode = doc.documentMode,
          supports_onhashchange = 'on' + str_hashchange in window && (doc_mode === undefined || doc_mode > 7);

        // Get location.hash (or what you'd expect location.hash to be) sans any
        // leading #. Thanks for making this necessary, Firefox!
        function get_fragment(url) {
            url = url || location.href;
            return '#' + url.replace(/^[^#]*#?(.*)$/, '$1');
        };

        // Method: jQuery.fn.hashchange
        // 
        // Bind a handler to the window.onhashchange event or trigger all bound
        // window.onhashchange event handlers. This behavior is consistent with
        // jQuery's built-in event handlers.
        // 
        // Usage:
        // 
        // > jQuery(window).hashchange( [ handler ] );
        // 
        // Arguments:
        // 
        //  handler - (Function) Optional handler to be bound to the hashchange
        //    event. This is a "shortcut" for the more verbose form:
        //    jQuery(window).bind( 'hashchange', handler ). If handler is omitted,
        //    all bound window.onhashchange event handlers will be triggered. This
        //    is a shortcut for the more verbose
        //    jQuery(window).trigger( 'hashchange' ). These forms are described in
        //    the <hashchange event> section.
        // 
        // Returns:
        // 
        //  (jQuery) The initial jQuery collection of elements.

        // Allow the "shortcut" format $(elem).hashchange( fn ) for binding and
        // $(elem).hashchange() for triggering, like jQuery does for built-in events.
        $.fn[str_hashchange] = function (fn) {
            return fn ? this.bind(str_hashchange, fn) : this.trigger(str_hashchange);
        };

        // Property: jQuery.fn.hashchange.delay
        // 
        // The numeric interval (in milliseconds) at which the <hashchange event>
        // polling loop executes. Defaults to 50.

        // Property: jQuery.fn.hashchange.domain
        // 
        // If you're setting document.domain in your JavaScript, and you want hash
        // history to work in IE6/7, not only must this property be set, but you must
        // also set document.domain BEFORE jQuery is loaded into the page. This
        // property is only applicable if you are supporting IE6/7 (or IE8 operating
        // in "IE7 compatibility" mode).
        // 
        // In addition, the <jQuery.fn.hashchange.src> property must be set to the
        // path of the included "document-domain.html" file, which can be renamed or
        // modified if necessary (note that the document.domain specified must be the
        // same in both your main JavaScript as well as in this file).
        // 
        // Usage:
        // 
        // jQuery.fn.hashchange.domain = document.domain;

        // Property: jQuery.fn.hashchange.src
        // 
        // If, for some reason, you need to specify an Iframe src file (for example,
        // when setting document.domain as in <jQuery.fn.hashchange.domain>), you can
        // do so using this property. Note that when using this property, history
        // won't be recorded in IE6/7 until the Iframe src file loads. This property
        // is only applicable if you are supporting IE6/7 (or IE8 operating in "IE7
        // compatibility" mode).
        // 
        // Usage:
        // 
        // jQuery.fn.hashchange.src = 'path/to/file.html';

        $.fn[str_hashchange].delay = 50;
        /*
        $.fn[ str_hashchange ].domain = null;
        $.fn[ str_hashchange ].src = null;
        */

        // Event: hashchange event
        // 
        // Fired when location.hash changes. In browsers that support it, the native
        // HTML5 window.onhashchange event is used, otherwise a polling loop is
        // initialized, running every <jQuery.fn.hashchange.delay> milliseconds to
        // see if the hash has changed. In IE6/7 (and IE8 operating in "IE7
        // compatibility" mode), a hidden Iframe is created to allow the back button
        // and hash-based history to work.
        // 
        // Usage as described in <jQuery.fn.hashchange>:
        // 
        // > // Bind an event handler.
        // > jQuery(window).hashchange( function(e) {
        // >   var hash = location.hash;
        // >   ...
        // > });
        // > 
        // > // Manually trigger the event handler.
        // > jQuery(window).hashchange();
        // 
        // A more verbose usage that allows for event namespacing:
        // 
        // > // Bind an event handler.
        // > jQuery(window).bind( 'hashchange', function(e) {
        // >   var hash = location.hash;
        // >   ...
        // > });
        // > 
        // > // Manually trigger the event handler.
        // > jQuery(window).trigger( 'hashchange' );
        // 
        // Additional Notes:
        // 
        // * The polling loop and Iframe are not created until at least one handler
        //   is actually bound to the 'hashchange' event.
        // * If you need the bound handler(s) to execute immediately, in cases where
        //   a location.hash exists on page load, via bookmark or page refresh for
        //   example, use jQuery(window).hashchange() or the more verbose 
        //   jQuery(window).trigger( 'hashchange' ).
        // * The event can be bound before DOM ready, but since it won't be usable
        //   before then in IE6/7 (due to the necessary Iframe), recommended usage is
        //   to bind it inside a DOM ready handler.

        // Override existing $.event.special.hashchange methods (allowing this plugin
        // to be defined after jQuery BBQ in BBQ's source code).
        special[str_hashchange] = $.extend(special[str_hashchange], {

            // Called only when the first 'hashchange' event is bound to window.
            setup: function () {
                // If window.onhashchange is supported natively, there's nothing to do..
                if (supports_onhashchange) { return false; }

                // Otherwise, we need to create our own. And we don't want to call this
                // until the user binds to the event, just in case they never do, since it
                // will create a polling loop and possibly even a hidden Iframe.
                $(fake_onhashchange.start);
            },

            // Called only when the last 'hashchange' event is unbound from window.
            teardown: function () {
                // If window.onhashchange is supported natively, there's nothing to do..
                if (supports_onhashchange) { return false; }

                // Otherwise, we need to stop ours (if possible).
                $(fake_onhashchange.stop);
            }

        });

        // fake_onhashchange does all the work of triggering the window.onhashchange
        // event for browsers that don't natively support it, including creating a
        // polling loop to watch for hash changes and in IE 6/7 creating a hidden
        // Iframe to enable back and forward.
        fake_onhashchange = (function () {
            var self = {},
              timeout_id,

              // Remember the initial hash so it doesn't get triggered immediately.
              last_hash = get_fragment(),

              fn_retval = function (val) { return val; },
              history_set = fn_retval,
              history_get = fn_retval;

            // Start the polling loop.
            self.start = function () {
                timeout_id || poll();
            };

            // Stop the polling loop.
            self.stop = function () {
                timeout_id && clearTimeout(timeout_id);
                timeout_id = undefined;
            };

            // This polling loop checks every $.fn.hashchange.delay milliseconds to see
            // if location.hash has changed, and triggers the 'hashchange' event on
            // window when necessary.
            function poll() {
                var hash = get_fragment(),
                  history_hash = history_get(last_hash);

                if (hash !== last_hash) {
                    history_set(last_hash = hash, history_hash);

                    $(window).trigger(str_hashchange);

                } else if (history_hash !== last_hash) {
                    location.href = location.href.replace(/#.*/, '') + history_hash;
                }

                timeout_id = setTimeout(poll, $.fn[str_hashchange].delay);
            };

            // vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
            // vvvvvvvvvvvvvvvvvvv REMOVE IF NOT SUPPORTING IE6/7/8 vvvvvvvvvvvvvvvvvvv
            // vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
            window.attachEvent && !window.addEventListener && !supports_onhashchange && (function () {
                // Not only do IE6/7 need the "magical" Iframe treatment, but so does IE8
                // when running in "IE7 compatibility" mode.

                var iframe,
                  iframe_src;

                // When the event is bound and polling starts in IE 6/7, create a hidden
                // Iframe for history handling.
                self.start = function () {
                    if (!iframe) {
                        iframe_src = $.fn[str_hashchange].src;
                        iframe_src = iframe_src && iframe_src + get_fragment();

                        // Create hidden Iframe. Attempt to make Iframe as hidden as possible
                        // by using techniques from http://www.paciellogroup.com/blog/?p=604.
                        iframe = $('<iframe tabindex="-1" title="empty"/>').hide()

                          // When Iframe has completely loaded, initialize the history and
                          // start polling.
                          .one('load', function () {
                              iframe_src || history_set(get_fragment());
                              poll();
                          })

                          // Load Iframe src if specified, otherwise nothing.
                          .attr('src', iframe_src || 'javascript:0')

                          // Append Iframe after the end of the body to prevent unnecessary
                          // initial page scrolling (yes, this works).
                          .insertAfter('body')[0].contentWindow;

                        // Whenever `document.title` changes, update the Iframe's title to
                        // prettify the back/next history menu entries. Since IE sometimes
                        // errors with "Unspecified error" the very first time this is set
                        // (yes, very useful) wrap this with a try/catch block.
                        doc.onpropertychange = function () {
                            try {
                                if (event.propertyName === 'title') {
                                    iframe.document.title = doc.title;
                                }
                            } catch (e) { }
                        };

                    }
                };

                // Override the "stop" method since an IE6/7 Iframe was created. Even
                // if there are no longer any bound event handlers, the polling loop
                // is still necessary for back/next to work at all!
                self.stop = fn_retval;

                // Get history by looking at the hidden Iframe's location.hash.
                history_get = function () {
                    return get_fragment(iframe.location.href);
                };

                // Set a new history item by opening and then closing the Iframe
                // document, *then* setting its location.hash. If document.domain has
                // been set, update that as well.
                history_set = function (hash, history_hash) {
                    var iframe_doc = iframe.document,
                      domain = $.fn[str_hashchange].domain;

                    if (hash !== history_hash) {
                        // Update Iframe with any initial `document.title` that might be set.
                        iframe_doc.title = doc.title;

                        // Opening the Iframe's document after it has been closed is what
                        // actually adds a history entry.
                        iframe_doc.open();

                        // Set document.domain for the Iframe document as well, if necessary.
                        domain && iframe_doc.write('\x3cscript>document.domain="' + domain + '"\x3c/script>');

                        iframe_doc.close();

                        // Update the Iframe's hash, for great justice.
                        iframe.location.hash = hash;
                    }
                };

            })();
            // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
            // ^^^^^^^^^^^^^^^^^^^ REMOVE IF NOT SUPPORTING IE6/7/8 ^^^^^^^^^^^^^^^^^^^
            // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

            return self;
        })();

    })(jQuery, this);

    (function ($) {
        $.mobile = {};
    }(jQuery));

    (function ($, window, undefined) {
        $.extend($.mobile, {

            // Version of the jQuery Mobile Framework
            version: "1.4.5",

            // Deprecated and no longer used in 1.4 remove in 1.5
            // Define the url parameter used for referencing widget-generated sub-pages.
            // Translates to example.html&ui-page=subpageIdentifier
            // hash segment before &ui-page= is used to make Ajax request
            subPageUrlKey: "ui-page",

            hideUrlBar: true,

            // Keepnative Selector
            keepNative: ":jqmData(role='none'), :jqmData(role='nojs')",

            // Deprecated in 1.4 remove in 1.5
            // Class assigned to page currently in view, and during transitions
            activePageClass: "ui-page-active",

            // Deprecated in 1.4 remove in 1.5
            // Class used for "active" button state, from CSS framework
            activeBtnClass: "ui-btn-active",

            // Deprecated in 1.4 remove in 1.5
            // Class used for "focus" form element state, from CSS framework
            focusClass: "ui-focus",

            // Automatically handle clicks and form submissions through Ajax, when same-domain
            ajaxEnabled: true,

            // Automatically load and show pages based on location.hash
            hashListeningEnabled: true,

            // disable to prevent jquery from bothering with links
            linkBindingEnabled: true,

            // Set default page transition - 'none' for no transitions
            defaultPageTransition: "fade",

            // Set maximum window width for transitions to apply - 'false' for no limit
            maxTransitionWidth: false,

            // Minimum scroll distance that will be remembered when returning to a page
            // Deprecated remove in 1.5
            minScrollBack: 0,

            // Set default dialog transition - 'none' for no transitions
            defaultDialogTransition: "pop",

            // Error response message - appears when an Ajax page request fails
            pageLoadErrorMessage: "Error Loading Page",

            // For error messages, which theme does the box use?
            pageLoadErrorMessageTheme: "a",

            // replace calls to window.history.back with phonegaps navigation helper
            // where it is provided on the window object
            phonegapNavigationEnabled: false,

            //automatically initialize the DOM when it's ready
            autoInitializePage: true,

            pushStateEnabled: true,

            // allows users to opt in to ignoring content by marking a parent element as
            // data-ignored
            ignoreContentEnabled: false,

            buttonMarkup: {
                hoverDelay: 200
            },

            // disable the alteration of the dynamic base tag or links in the case
            // that a dynamic base tag isn't supported
            dynamicBaseEnabled: true,

            // default the property to remove dependency on assignment in init module
            pageContainer: $(),

            //enable cross-domain page support
            allowCrossDomainPages: false,

            dialogHashKey: "&ui-state=dialog"
        });
    })(jQuery, this);

    (function ($, window, undefined) {
        var nsNormalizeDict = {},
            oldFind = $.find,
            rbrace = /(?:\{[\s\S]*\}|\[[\s\S]*\])$/,
            jqmDataRE = /:jqmData\(([^)]*)\)/g;

        $.extend($.mobile, {

            // Namespace used framework-wide for data-attrs. Default is no namespace

            ns: "",

            // Retrieve an attribute from an element and perform some massaging of the value

            getAttribute: function (element, key) {
                var data;

                element = element.jquery ? element[0] : element;

                if (element && element.getAttribute) {
                    data = element.getAttribute("data-" + $.mobile.ns + key);
                }

                // Copied from core's src/data.js:dataAttr()
                // Convert from a string to a proper data type
                try {
                    data = data === "true" ? true :
                        data === "false" ? false :
                        data === "null" ? null :
                        // Only convert to a number if it doesn't change the string
                        +data + "" === data ? +data :
                        rbrace.test(data) ? JSON.parse(data) :
                        data;
                } catch (err) { }

                return data;
            },

            // Expose our cache for testing purposes.
            nsNormalizeDict: nsNormalizeDict,

            // Take a data attribute property, prepend the namespace
            // and then camel case the attribute string. Add the result
            // to our nsNormalizeDict so we don't have to do this again.
            nsNormalize: function (prop) {
                return nsNormalizeDict[prop] ||
                    (nsNormalizeDict[prop] = $.camelCase($.mobile.ns + prop));
            },

            // Find the closest javascript page element to gather settings data jsperf test
            // http://jsperf.com/single-complex-selector-vs-many-complex-selectors/edit
            // possibly naive, but it shows that the parsing overhead for *just* the page selector vs
            // the page and dialog selector is negligable. This could probably be speed up by
            // doing a similar parent node traversal to the one found in the inherited theme code above
            closestPageData: function ($target) {
                return $target
                    .closest(":jqmData(role='page'), :jqmData(role='dialog')")
                    .data("mobile-page");
            }

        });

        // Mobile version of data and removeData and hasData methods
        // ensures all data is set and retrieved using jQuery Mobile's data namespace
        $.fn.jqmData = function (prop, value) {
            var result;
            if (typeof prop !== "undefined") {
                if (prop) {
                    prop = $.mobile.nsNormalize(prop);
                }

                // undefined is permitted as an explicit input for the second param
                // in this case it returns the value and does not set it to undefined
                if (arguments.length < 2 || value === undefined) {
                    result = this.data(prop);
                } else {
                    result = this.data(prop, value);
                }
            }
            return result;
        };

        $.jqmData = function (elem, prop, value) {
            var result;
            if (typeof prop !== "undefined") {
                result = $.data(elem, prop ? $.mobile.nsNormalize(prop) : prop, value);
            }
            return result;
        };

        $.fn.jqmRemoveData = function (prop) {
            return this.removeData($.mobile.nsNormalize(prop));
        };

        $.jqmRemoveData = function (elem, prop) {
            return $.removeData(elem, $.mobile.nsNormalize(prop));
        };

        $.find = function (selector, context, ret, extra) {
            if (selector.indexOf(":jqmData") > -1) {
                selector = selector.replace(jqmDataRE, "[data-" + ($.mobile.ns || "") + "$1]");
            }

            return oldFind.call(this, selector, context, ret, extra);
        };

        $.extend($.find, oldFind);

    })(jQuery, this);

    /*!
     * jQuery UI Core c0ab71056b936627e8a7821f03c044aec6280a40
     * http://jqueryui.com
     *
     * Copyright 2013 jQuery Foundation and other contributors
     * Released under the MIT license.
     * http://jquery.org/license
     *
     * http://api.jqueryui.com/category/ui-core/
     */
    (function ($, undefined) {

        var uuid = 0,
            runiqueId = /^ui-id-\d+$/;

        // $.ui might exist from components with no dependencies, e.g., $.ui.position
        $.ui = $.ui || {};

        $.extend($.ui, {
            version: "c0ab71056b936627e8a7821f03c044aec6280a40",

            keyCode: {
                BACKSPACE: 8,
                COMMA: 188,
                DELETE: 46,
                DOWN: 40,
                END: 35,
                ENTER: 13,
                ESCAPE: 27,
                HOME: 36,
                LEFT: 37,
                PAGE_DOWN: 34,
                PAGE_UP: 33,
                PERIOD: 190,
                RIGHT: 39,
                SPACE: 32,
                TAB: 9,
                UP: 38
            }
        });

        // plugins
        $.fn.extend({
            focus: (function (orig) {
                return function (delay, fn) {
                    return typeof delay === "number" ?
                        this.each(function () {
                            var elem = this;
                            setTimeout(function () {
                                $(elem).focus();
                                if (fn) {
                                    fn.call(elem);
                                }
                            }, delay);
                        }) :
                        orig.apply(this, arguments);
                };
            })($.fn.focus),

            scrollParent: function () {
                var scrollParent;
                if (($.ui.ie && (/(static|relative)/).test(this.css("position"))) || (/absolute/).test(this.css("position"))) {
                    scrollParent = this.parents().filter(function () {
                        return (/(relative|absolute|fixed)/).test($.css(this, "position")) && (/(auto|scroll)/).test($.css(this, "overflow") + $.css(this, "overflow-y") + $.css(this, "overflow-x"));
                    }).eq(0);
                } else {
                    scrollParent = this.parents().filter(function () {
                        return (/(auto|scroll)/).test($.css(this, "overflow") + $.css(this, "overflow-y") + $.css(this, "overflow-x"));
                    }).eq(0);
                }

                return (/fixed/).test(this.css("position")) || !scrollParent.length ? $(this[0].ownerDocument || document) : scrollParent;
            },

            uniqueId: function () {
                return this.each(function () {
                    if (!this.id) {
                        this.id = "ui-id-" + (++uuid);
                    }
                });
            },

            removeUniqueId: function () {
                return this.each(function () {
                    if (runiqueId.test(this.id)) {
                        $(this).removeAttr("id");
                    }
                });
            }
        });

        // selectors
        function focusable(element, isTabIndexNotNaN) {
            var map, mapName, img,
                nodeName = element.nodeName.toLowerCase();
            if ("area" === nodeName) {
                map = element.parentNode;
                mapName = map.name;
                if (!element.href || !mapName || map.nodeName.toLowerCase() !== "map") {
                    return false;
                }
                img = $("img[usemap=#" + mapName + "]")[0];
                return !!img && visible(img);
            }
            return (/input|select|textarea|button|object/.test(nodeName) ?
                !element.disabled :
                "a" === nodeName ?
                    element.href || isTabIndexNotNaN :
                    isTabIndexNotNaN) &&
                // the element and all of its ancestors must be visible
                visible(element);
        }

        function visible(element) {
            return $.expr.filters.visible(element) &&
                !$(element).parents().addBack().filter(function () {
                    return $.css(this, "visibility") === "hidden";
                }).length;
        }

        $.extend($.expr[":"], {
            data: $.expr.createPseudo ?
                $.expr.createPseudo(function (dataName) {
                    return function (elem) {
                        return !!$.data(elem, dataName);
                    };
                }) :
                // support: jQuery <1.8
                function (elem, i, match) {
                    return !!$.data(elem, match[3]);
                },

            focusable: function (element) {
                return focusable(element, !isNaN($.attr(element, "tabindex")));
            },

            tabbable: function (element) {
                var tabIndex = $.attr(element, "tabindex"),
                    isTabIndexNaN = isNaN(tabIndex);
                return (isTabIndexNaN || tabIndex >= 0) && focusable(element, !isTabIndexNaN);
            }
        });

        // support: jQuery <1.8
        if (!$("<a>").outerWidth(1).jquery) {
            $.each(["Width", "Height"], function (i, name) {
                var side = name === "Width" ? ["Left", "Right"] : ["Top", "Bottom"],
                    type = name.toLowerCase(),
                    orig = {
                        innerWidth: $.fn.innerWidth,
                        innerHeight: $.fn.innerHeight,
                        outerWidth: $.fn.outerWidth,
                        outerHeight: $.fn.outerHeight
                    };

                function reduce(elem, size, border, margin) {
                    $.each(side, function () {
                        size -= parseFloat($.css(elem, "padding" + this)) || 0;
                        if (border) {
                            size -= parseFloat($.css(elem, "border" + this + "Width")) || 0;
                        }
                        if (margin) {
                            size -= parseFloat($.css(elem, "margin" + this)) || 0;
                        }
                    });
                    return size;
                }

                $.fn["inner" + name] = function (size) {
                    if (size === undefined) {
                        return orig["inner" + name].call(this);
                    }

                    return this.each(function () {
                        $(this).css(type, reduce(this, size) + "px");
                    });
                };

                $.fn["outer" + name] = function (size, margin) {
                    if (typeof size !== "number") {
                        return orig["outer" + name].call(this, size);
                    }

                    return this.each(function () {
                        $(this).css(type, reduce(this, size, true, margin) + "px");
                    });
                };
            });
        }

        // support: jQuery <1.8
        if (!$.fn.addBack) {
            $.fn.addBack = function (selector) {
                return this.add(selector == null ?
                    this.prevObject : this.prevObject.filter(selector)
                );
            };
        }

        // support: jQuery 1.6.1, 1.6.2 (http://bugs.jquery.com/ticket/9413)
        if ($("<a>").data("a-b", "a").removeData("a-b").data("a-b")) {
            $.fn.removeData = (function (removeData) {
                return function (key) {
                    if (arguments.length) {
                        return removeData.call(this, $.camelCase(key));
                    } else {
                        return removeData.call(this);
                    }
                };
            })($.fn.removeData);
        }





        // deprecated
        $.ui.ie = !!/msie [\w.]+/.exec(navigator.userAgent.toLowerCase());

        $.support.selectstart = "onselectstart" in document.createElement("div");
        $.fn.extend({
            disableSelection: function () {
                return this.bind(($.support.selectstart ? "selectstart" : "mousedown") +
                    ".ui-disableSelection", function (event) {
                        event.preventDefault();
                    });
            },

            enableSelection: function () {
                return this.unbind(".ui-disableSelection");
            },

            zIndex: function (zIndex) {
                if (zIndex !== undefined) {
                    return this.css("zIndex", zIndex);
                }

                if (this.length) {
                    var elem = $(this[0]), position, value;
                    while (elem.length && elem[0] !== document) {
                        // Ignore z-index if position is set to a value where z-index is ignored by the browser
                        // This makes behavior of this function consistent across browsers
                        // WebKit always returns auto if the element is positioned
                        position = elem.css("position");
                        if (position === "absolute" || position === "relative" || position === "fixed") {
                            // IE returns 0 when zIndex is not specified
                            // other browsers return a string
                            // we ignore the case of nested elements with an explicit value of 0
                            // <div style="z-index: -10;"><div style="z-index: 0;"></div></div>
                            value = parseInt(elem.css("zIndex"), 10);
                            if (!isNaN(value) && value !== 0) {
                                return value;
                            }
                        }
                        elem = elem.parent();
                    }
                }

                return 0;
            }
        });

        // $.ui.plugin is deprecated. Use $.widget() extensions instead.
        $.ui.plugin = {
            add: function (module, option, set) {
                var i,
                    proto = $.ui[module].prototype;
                for (i in set) {
                    proto.plugins[i] = proto.plugins[i] || [];
                    proto.plugins[i].push([option, set[i]]);
                }
            },
            call: function (instance, name, args, allowDisconnected) {
                var i,
                    set = instance.plugins[name];

                if (!set) {
                    return;
                }

                if (!allowDisconnected && (!instance.element[0].parentNode || instance.element[0].parentNode.nodeType === 11)) {
                    return;
                }

                for (i = 0; i < set.length; i++) {
                    if (instance.options[set[i][0]]) {
                        set[i][1].apply(instance.element, args);
                    }
                }
            }
        };

    })(jQuery);

    (function ($, window, undefined) {

        // Subtract the height of external toolbars from the page height, if the page does not have
        // internal toolbars of the same type. We take care to use the widget options if we find a
        // widget instance and the element's data-attributes otherwise.
        var compensateToolbars = function (page, desiredHeight) {
            var pageParent = page.parent(),
                toolbarsAffectingHeight = [],

                // We use this function to filter fixed toolbars with option updatePagePadding set to
                // true (which is the default) from our height subtraction, because fixed toolbars with
                // option updatePagePadding set to true compensate for their presence by adding padding
                // to the active page. We want to avoid double-counting by also subtracting their
                // height from the desired page height.
                noPadders = function () {
                    var theElement = $(this),
                        widgetOptions = $.mobile.toolbar && theElement.data("mobile-toolbar") ?
                            theElement.toolbar("option") : {
                                position: theElement.attr("data-" + $.mobile.ns + "position"),
                                updatePagePadding: (theElement.attr("data-" + $.mobile.ns +
                                    "update-page-padding") !== false)
                            };

                    return !(widgetOptions.position === "fixed" &&
                        widgetOptions.updatePagePadding === true);
                },
                externalHeaders = pageParent.children(":jqmData(role='header')").filter(noPadders),
                internalHeaders = page.children(":jqmData(role='header')"),
                externalFooters = pageParent.children(":jqmData(role='footer')").filter(noPadders),
                internalFooters = page.children(":jqmData(role='footer')");

            // If we have no internal headers, but we do have external headers, then their height
            // reduces the page height
            if (internalHeaders.length === 0 && externalHeaders.length > 0) {
                toolbarsAffectingHeight = toolbarsAffectingHeight.concat(externalHeaders.toArray());
            }

            // If we have no internal footers, but we do have external footers, then their height
            // reduces the page height
            if (internalFooters.length === 0 && externalFooters.length > 0) {
                toolbarsAffectingHeight = toolbarsAffectingHeight.concat(externalFooters.toArray());
            }

            $.each(toolbarsAffectingHeight, function (index, value) {
                desiredHeight -= $(value).outerHeight();
            });

            // Height must be at least zero
            return Math.max(0, desiredHeight);
        };

        $.extend($.mobile, {
            // define the window and the document objects
            window: $(window),
            document: $(document),

            // TODO: Remove and use $.ui.keyCode directly
            keyCode: $.ui.keyCode,

            // Place to store various widget extensions
            behaviors: {},

            // Scroll page vertically: scroll to 0 to hide iOS address bar, or pass a Y value
            silentScroll: function (ypos) {
                if ($.type(ypos) !== "number") {
                    ypos = $.mobile.defaultHomeScroll;
                }

                // prevent scrollstart and scrollstop events
                $.event.special.scrollstart.enabled = false;

                setTimeout(function () {
                    window.scrollTo(0, ypos);
                    $.mobile.document.trigger("silentscroll", { x: 0, y: ypos });
                }, 20);

                setTimeout(function () {
                    $.event.special.scrollstart.enabled = true;
                }, 150);
            },

            getClosestBaseUrl: function (ele) {
                // Find the closest page and extract out its url.
                var url = $(ele).closest(".ui-page").jqmData("url"),
                    base = $.mobile.path.documentBase.hrefNoHash;

                if (!$.mobile.dynamicBaseEnabled || !url || !$.mobile.path.isPath(url)) {
                    url = base;
                }

                return $.mobile.path.makeUrlAbsolute(url, base);
            },
            removeActiveLinkClass: function (forceRemoval) {
                if (!!$.mobile.activeClickedLink &&
                    (!$.mobile.activeClickedLink.closest("." + $.mobile.activePageClass).length ||
                        forceRemoval)) {

                    $.mobile.activeClickedLink.removeClass($.mobile.activeBtnClass);
                }
                $.mobile.activeClickedLink = null;
            },

            // DEPRECATED in 1.4
            // Find the closest parent with a theme class on it. Note that
            // we are not using $.fn.closest() on purpose here because this
            // method gets called quite a bit and we need it to be as fast
            // as possible.
            getInheritedTheme: function (el, defaultTheme) {
                var e = el[0],
                    ltr = "",
                    re = /ui-(bar|body|overlay)-([a-z])\b/,
                    c, m;
                while (e) {
                    c = e.className || "";
                    if (c && (m = re.exec(c)) && (ltr = m[2])) {
                        // We found a parent with a theme class
                        // on it so bail from this loop.
                        break;
                    }

                    e = e.parentNode;
                }
                // Return the theme letter we found, if none, return the
                // specified default.
                return ltr || defaultTheme || "a";
            },

            enhanceable: function (elements) {
                return this.haveParents(elements, "enhance");
            },

            hijackable: function (elements) {
                return this.haveParents(elements, "ajax");
            },

            haveParents: function (elements, attr) {
                if (!$.mobile.ignoreContentEnabled) {
                    return elements;
                }

                var count = elements.length,
                    $newSet = $(),
                    e, $element, excluded,
                    i, c;

                for (i = 0; i < count; i++) {
                    $element = elements.eq(i);
                    excluded = false;
                    e = elements[i];

                    while (e) {
                        c = e.getAttribute ? e.getAttribute("data-" + $.mobile.ns + attr) : "";

                        if (c === "false") {
                            excluded = true;
                            break;
                        }

                        e = e.parentNode;
                    }

                    if (!excluded) {
                        $newSet = $newSet.add($element);
                    }
                }

                return $newSet;
            },

            getScreenHeight: function () {
                // Native innerHeight returns more accurate value for this across platforms,
                // jQuery version is here as a normalized fallback for platforms like Symbian
                return window.innerHeight || $.mobile.window.height();
            },

            //simply set the active page's minimum height to screen height, depending on orientation
            resetActivePageHeight: function (height) {
                var page = $("." + $.mobile.activePageClass),
                    pageHeight = page.height(),
                    pageOuterHeight = page.outerHeight(true);

                height = compensateToolbars(page,
                    (typeof height === "number") ? height : $.mobile.getScreenHeight());

                // Remove any previous min-height setting
                page.css("min-height", "");

                // Set the minimum height only if the height as determined by CSS is insufficient
                if (page.height() < height) {
                    page.css("min-height", height - (pageOuterHeight - pageHeight));
                }
            },

            loading: function () {
                // If this is the first call to this function, instantiate a loader widget
                var loader = this.loading._widget || $($.mobile.loader.prototype.defaultHtml).loader(),

                    // Call the appropriate method on the loader
                    returnValue = loader.loader.apply(loader, arguments);

                // Make sure the loader is retained for future calls to this function.
                this.loading._widget = loader;

                return returnValue;
            }
        });

        $.addDependents = function (elem, newDependents) {
            var $elem = $(elem),
                dependents = $elem.jqmData("dependents") || $();

            $elem.jqmData("dependents", $(dependents).add(newDependents));
        };

        // plugins
        $.fn.extend({
            removeWithDependents: function () {
                $.removeWithDependents(this);
            },

            // Enhance child elements
            enhanceWithin: function () {
                var index,
                    widgetElements = {},
                    keepNative = $.mobile.page.prototype.keepNativeSelector(),
                    that = this;

                // Add no js class to elements
                if ($.mobile.nojs) {
                    $.mobile.nojs(this);
                }

                // Bind links for ajax nav
                if ($.mobile.links) {
                    $.mobile.links(this);
                }

                // Degrade inputs for styleing
                if ($.mobile.degradeInputsWithin) {
                    $.mobile.degradeInputsWithin(this);
                }

                // Run buttonmarkup
                if ($.fn.buttonMarkup) {
                    this.find($.fn.buttonMarkup.initSelector).not(keepNative)
                    .jqmEnhanceable().buttonMarkup();
                }

                // Add classes for fieldContain
                if ($.fn.fieldcontain) {
                    this.find(":jqmData(role='fieldcontain')").not(keepNative)
                    .jqmEnhanceable().fieldcontain();
                }

                // Enhance widgets
                $.each($.mobile.widgets, function (name, constructor) {

                    // If initSelector not false find elements
                    if (constructor.initSelector) {

                        // Filter elements that should not be enhanced based on parents
                        var elements = $.mobile.enhanceable(that.find(constructor.initSelector));

                        // If any matching elements remain filter ones with keepNativeSelector
                        if (elements.length > 0) {

                            // $.mobile.page.prototype.keepNativeSelector is deprecated this is just for backcompat
                            // Switch to $.mobile.keepNative in 1.5 which is just a value not a function
                            elements = elements.not(keepNative);
                        }

                        // Enhance whatever is left
                        if (elements.length > 0) {
                            widgetElements[constructor.prototype.widgetName] = elements;
                        }
                    }
                });

                for (index in widgetElements) {
                    widgetElements[index][index]();
                }

                return this;
            },

            addDependents: function (newDependents) {
                $.addDependents(this, newDependents);
            },

            // note that this helper doesn't attempt to handle the callback
            // or setting of an html element's text, its only purpose is
            // to return the html encoded version of the text in all cases. (thus the name)
            getEncodedText: function () {
                return $("<a>").text(this.text()).html();
            },

            // fluent helper function for the mobile namespaced equivalent
            jqmEnhanceable: function () {
                return $.mobile.enhanceable(this);
            },

            jqmHijackable: function () {
                return $.mobile.hijackable(this);
            }
        });

        $.removeWithDependents = function (nativeElement) {
            var element = $(nativeElement);

            (element.jqmData("dependents") || $()).remove();
            element.remove();
        };
        $.addDependents = function (nativeElement, newDependents) {
            var element = $(nativeElement),
                dependents = element.jqmData("dependents") || $();

            element.jqmData("dependents", $(dependents).add(newDependents));
        };

        $.find.matches = function (expr, set) {
            return $.find(expr, null, null, set);
        };

        $.find.matchesSelector = function (node, expr) {
            return $.find(expr, null, null, [node]).length > 0;
        };

    })(jQuery, this);


    (function ($, undefined) {

        /*! matchMedia() polyfill - Test a CSS media type/query in JS. Authors & copyright (c) 2012: Scott Jehl, Paul Irish, Nicholas Zakas. Dual MIT/BSD license */
        window.matchMedia = window.matchMedia || (function (doc, undefined) {

            var bool,
                docElem = doc.documentElement,
                refNode = docElem.firstElementChild || docElem.firstChild,
                // fakeBody required for <FF4 when executed in <head>
                fakeBody = doc.createElement("body"),
                div = doc.createElement("div");

            div.id = "mq-test-1";
            div.style.cssText = "position:absolute;top:-100em";
            fakeBody.style.background = "none";
            fakeBody.appendChild(div);

            return function (q) {

                div.innerHTML = "&shy;<style media=\"" + q + "\"> #mq-test-1 { width: 42px; }</style>";

                docElem.insertBefore(fakeBody, refNode);
                bool = div.offsetWidth === 42;
                docElem.removeChild(fakeBody);

                return {
                    matches: bool,
                    media: q
                };

            };

        }(document));

        // $.mobile.media uses matchMedia to return a boolean.
        $.mobile.media = function (q) {
            return window.matchMedia(q).matches;
        };

    })(jQuery);

    (function ($, undefined) {
        var support = {
            touch: "ontouchend" in document
        };

        $.mobile.support = $.mobile.support || {};
        $.extend($.support, support);
        $.extend($.mobile.support, support);
    }(jQuery));

    (function ($, undefined) {
        $.extend($.support, {
            orientation: "orientation" in window && "onorientationchange" in window
        });
    }(jQuery));

    (function ($, undefined) {

        // thx Modernizr
        function propExists(prop) {
            var uc_prop = prop.charAt(0).toUpperCase() + prop.substr(1),
                props = (prop + " " + vendors.join(uc_prop + " ") + uc_prop).split(" "),
                v;

            for (v in props) {
                if (fbCSS[props[v]] !== undefined) {
                    return true;
                }
            }
        }

        var fakeBody = $("<body>").prependTo("html"),
            fbCSS = fakeBody[0].style,
            vendors = ["Webkit", "Moz", "O"],
            webos = "palmGetResource" in window, //only used to rule out scrollTop
            operamini = window.operamini && ({}).toString.call(window.operamini) === "[object OperaMini]",
            bb = window.blackberry && !propExists("-webkit-transform"), //only used to rule out box shadow, as it's filled opaque on BB 5 and lower
            nokiaLTE7_3;

        // inline SVG support test
        function inlineSVG() {
            // Thanks Modernizr & Erik Dahlstrom
            var w = window,
                svg = !!w.document.createElementNS && !!w.document.createElementNS("http://www.w3.org/2000/svg", "svg").createSVGRect && !(w.opera && navigator.userAgent.indexOf("Chrome") === -1),
                support = function (data) {
                    if (!(data && svg)) {
                        $("html").addClass("ui-nosvg");
                    }
                },
                img = new w.Image();

            img.onerror = function () {
                support(false);
            };
            img.onload = function () {
                support(img.width === 1 && img.height === 1);
            };
            img.src = "";
        }

        function transform3dTest() {
            var mqProp = "transform-3d",
                // Because the `translate3d` test below throws false positives in Android:
                ret = $.mobile.media("(-" + vendors.join("-" + mqProp + "),(-") + "-" + mqProp + "),(" + mqProp + ")"),
                el, transforms, t;

            if (ret) {
                return !!ret;
            }

            el = document.createElement("div");
            transforms = {
                // We鈥檙e omitting Opera for the time being; MS uses unprefixed.
                "MozTransform": "-moz-transform",
                "transform": "transform"
            };

            fakeBody.append(el);

            for (t in transforms) {
                if (el.style[t] !== undefined) {
                    el.style[t] = "translate3d( 100px, 1px, 1px )";
                    ret = window.getComputedStyle(el).getPropertyValue(transforms[t]);
                }
            }
            return (!!ret && ret !== "none");
        }

        // Test for dynamic-updating base tag support ( allows us to avoid href,src attr rewriting )
        function baseTagTest() {
            var fauxBase = location.protocol + "//" + location.host + location.pathname + "ui-dir/",
                base = $("head base"),
                fauxEle = null,
                href = "",
                link, rebase;

            if (!base.length) {
                base = fauxEle = $("<base>", { "href": fauxBase }).appendTo("head");
            } else {
                href = base.attr("href");
            }

            link = $("<a href='testurl' />").prependTo(fakeBody);
            rebase = link[0].href;
            base[0].href = href || location.pathname;

            if (fauxEle) {
                fauxEle.remove();
            }
            return rebase.indexOf(fauxBase) === 0;
        }

        // Thanks Modernizr
        function cssPointerEventsTest() {
            var element = document.createElement("x"),
                documentElement = document.documentElement,
                getComputedStyle = window.getComputedStyle,
                supports;

            if (!("pointerEvents" in element.style)) {
                return false;
            }

            element.style.pointerEvents = "auto";
            element.style.pointerEvents = "x";
            documentElement.appendChild(element);
            supports = getComputedStyle &&
            getComputedStyle(element, "").pointerEvents === "auto";
            documentElement.removeChild(element);
            return !!supports;
        }

        function boundingRect() {
            var div = document.createElement("div");
            return typeof div.getBoundingClientRect !== "undefined";
        }

        // non-UA-based IE version check by James Padolsey, modified by jdalton - from http://gist.github.com/527683
        // allows for inclusion of IE 6+, including Windows Mobile 7
        $.extend($.mobile, { browser: {} });
        $.mobile.browser.oldIE = (function () {
            var v = 3,
                div = document.createElement("div"),
                a = div.all || [];

            do {
                div.innerHTML = "<!--[if gt IE " + (++v) + "]><br><![endif]-->";
            } while (a[0]);

            return v > 4 ? v : !v;
        })();

        function fixedPosition() {
            var w = window,
                ua = navigator.userAgent,
                platform = navigator.platform,
                // Rendering engine is Webkit, and capture major version
                wkmatch = ua.match(/AppleWebKit\/([0-9]+)/),
                wkversion = !!wkmatch && wkmatch[1],
                ffmatch = ua.match(/Fennec\/([0-9]+)/),
                ffversion = !!ffmatch && ffmatch[1],
                operammobilematch = ua.match(/Opera Mobi\/([0-9]+)/),
                omversion = !!operammobilematch && operammobilematch[1];

            if (
                // iOS 4.3 and older : Platform is iPhone/Pad/Touch and Webkit version is less than 534 (ios5)
                ((platform.indexOf("iPhone") > -1 || platform.indexOf("iPad") > -1 || platform.indexOf("iPod") > -1) && wkversion && wkversion < 534) ||
                // Opera Mini
                (w.operamini && ({}).toString.call(w.operamini) === "[object OperaMini]") ||
                (operammobilematch && omversion < 7458) ||
                //Android lte 2.1: Platform is Android and Webkit version is less than 533 (Android 2.2)
                (ua.indexOf("Android") > -1 && wkversion && wkversion < 533) ||
                // Firefox Mobile before 6.0 -
                (ffversion && ffversion < 6) ||
                // WebOS less than 3
                ("palmGetResource" in window && wkversion && wkversion < 534) ||
                // MeeGo
                (ua.indexOf("MeeGo") > -1 && ua.indexOf("NokiaBrowser/8.5.0") > -1)) {
                return false;
            }

            return true;
        }

        $.extend($.support, {
            // Note, Chrome for iOS has an extremely quirky implementation of popstate.
            // We've chosen to take the shortest path to a bug fix here for issue #5426
            // See the following link for information about the regex chosen
            // https://developers.google.com/chrome/mobile/docs/user-agent#chrome_for_ios_user-agent
            pushState: "pushState" in history &&
                "replaceState" in history &&
                // When running inside a FF iframe, calling replaceState causes an error
                !(window.navigator.userAgent.indexOf("Firefox") >= 0 && window.top !== window) &&
                (window.navigator.userAgent.search(/CriOS/) === -1),

            mediaquery: $.mobile.media("only all"),
            cssPseudoElement: !!propExists("content"),
            touchOverflow: !!propExists("overflowScrolling"),
            cssTransform3d: transform3dTest(),
            boxShadow: !!propExists("boxShadow") && !bb,
            fixedPosition: fixedPosition(),
            scrollTop: ("pageXOffset" in window ||
                "scrollTop" in document.documentElement ||
                "scrollTop" in fakeBody[0]) && !webos && !operamini,

            dynamicBaseTag: baseTagTest(),
            cssPointerEvents: cssPointerEventsTest(),
            boundingRect: boundingRect(),
            inlineSVG: inlineSVG
        });

        fakeBody.remove();

        // $.mobile.ajaxBlacklist is used to override ajaxEnabled on platforms that have known conflicts with hash history updates (BB5, Symbian)
        // or that generally work better browsing in regular http for full page refreshes (Opera Mini)
        // Note: This detection below is used as a last resort.
        // We recommend only using these detection methods when all other more reliable/forward-looking approaches are not possible
        nokiaLTE7_3 = (function () {

            var ua = window.navigator.userAgent;

            //The following is an attempt to match Nokia browsers that are running Symbian/s60, with webkit, version 7.3 or older
            return ua.indexOf("Nokia") > -1 &&
                    (ua.indexOf("Symbian/3") > -1 || ua.indexOf("Series60/5") > -1) &&
                    ua.indexOf("AppleWebKit") > -1 &&
                    ua.match(/(BrowserNG|NokiaBrowser)\/7\.[0-3]/);
        })();

        // Support conditions that must be met in order to proceed
        // default enhanced qualifications are media query support OR IE 7+

        $.mobile.gradeA = function () {
            return (($.support.mediaquery && $.support.cssPseudoElement) || $.mobile.browser.oldIE && $.mobile.browser.oldIE >= 8) && ($.support.boundingRect || $.fn.jquery.match(/1\.[0-7+]\.[0-9+]?/) !== null);
        };

        $.mobile.ajaxBlacklist =
                    // BlackBerry browsers, pre-webkit
                    window.blackberry && !window.WebKitPoint ||
                    // Opera Mini
                    operamini ||
                    // Symbian webkits pre 7.3
                    nokiaLTE7_3;

        // Lastly, this workaround is the only way we've found so far to get pre 7.3 Symbian webkit devices
        // to render the stylesheets when they're referenced before this script, as we'd recommend doing.
        // This simply reappends the CSS in place, which for some reason makes it apply
        if (nokiaLTE7_3) {
            $(function () {
                $("head link[rel='stylesheet']").attr("rel", "alternate stylesheet").attr("rel", "stylesheet");
            });
        }

        // For ruling out shadows via css
        if (!$.support.boxShadow) {
            $("html").addClass("ui-noboxshadow");
        }

    })(jQuery);


    (function ($, undefined) {
        var $win = $.mobile.window, self,
            dummyFnToInitNavigate = function () {
            };

        $.event.special.beforenavigate = {
            setup: function () {
                $win.on("navigate", dummyFnToInitNavigate);
            },

            teardown: function () {
                $win.off("navigate", dummyFnToInitNavigate);
            }
        };

        $.event.special.navigate = self = {
            bound: false,

            pushStateEnabled: true,

            originalEventName: undefined,

            // If pushstate support is present and push state support is defined to
            // be true on the mobile namespace.
            isPushStateEnabled: function () {
                return $.support.pushState &&
                    $.mobile.pushStateEnabled === true &&
                    this.isHashChangeEnabled();
            },

            // !! assumes mobile namespace is present
            isHashChangeEnabled: function () {
                return $.mobile.hashListeningEnabled === true;
            },

            // TODO a lot of duplication between popstate and hashchange
            popstate: function (event) {
                var newEvent = new $.Event("navigate"),
                    beforeNavigate = new $.Event("beforenavigate"),
                    state = event.originalEvent.state || {};

                beforeNavigate.originalEvent = event;
                $win.trigger(beforeNavigate);

                if (beforeNavigate.isDefaultPrevented()) {
                    return;
                }

                if (event.historyState) {
                    $.extend(state, event.historyState);
                }

                // Make sure the original event is tracked for the end
                // user to inspect incase they want to do something special
                newEvent.originalEvent = event;

                // NOTE we let the current stack unwind because any assignment to
                //      location.hash will stop the world and run this event handler. By
                //      doing this we create a similar behavior to hashchange on hash
                //      assignment
                setTimeout(function () {
                    $win.trigger(newEvent, {
                        state: state
                    });
                }, 0);
            },

            hashchange: function (event /*, data */) {
                var newEvent = new $.Event("navigate"),
                    beforeNavigate = new $.Event("beforenavigate");

                beforeNavigate.originalEvent = event;
                $win.trigger(beforeNavigate);

                if (beforeNavigate.isDefaultPrevented()) {
                    return;
                }

                // Make sure the original event is tracked for the end
                // user to inspect incase they want to do something special
                newEvent.originalEvent = event;

                // Trigger the hashchange with state provided by the user
                // that altered the hash
                $win.trigger(newEvent, {
                    // Users that want to fully normalize the two events
                    // will need to do history management down the stack and
                    // add the state to the event before this binding is fired
                    // TODO consider allowing for the explicit addition of callbacks
                    //      to be fired before this value is set to avoid event timing issues
                    state: event.hashchangeState || {}
                });
            },

            // TODO We really only want to set this up once
            //      but I'm not clear if there's a beter way to achieve
            //      this with the jQuery special event structure
            setup: function ( /* data, namespaces */) {
                if (self.bound) {
                    return;
                }

                self.bound = true;

                if (self.isPushStateEnabled()) {
                    self.originalEventName = "popstate";
                    $win.bind("popstate.navigate", self.popstate);
                } else if (self.isHashChangeEnabled()) {
                    self.originalEventName = "hashchange";
                    $win.bind("hashchange.navigate", self.hashchange);
                }
            }
        };
    })(jQuery);



    // throttled resize event
    (function ($) {
        $.event.special.throttledresize = {
            setup: function () {
                $(this).bind("resize", handler);
            },
            teardown: function () {
                $(this).unbind("resize", handler);
            }
        };

        var throttle = 250,
			handler = function () {
			    curr = (new Date()).getTime();
			    diff = curr - lastCall;

			    if (diff >= throttle) {

			        lastCall = curr;
			        $(this).trigger("throttledresize");

			    } else {

			        if (heldCall) {
			            clearTimeout(heldCall);
			        }

			        // Promise a held call will still execute
			        heldCall = setTimeout(handler, throttle - diff);
			    }
			},
			lastCall = 0,
			heldCall,
			curr,
			diff;
    })(jQuery);


    (function ($, window) {
        var win = $(window),
            event_name = "orientationchange",
            get_orientation,
            last_orientation,
            initial_orientation_is_landscape,
            initial_orientation_is_default,
            portrait_map = { "0": true, "180": true },
            ww, wh, landscape_threshold;

        // It seems that some device/browser vendors use window.orientation values 0 and 180 to
        // denote the "default" orientation. For iOS devices, and most other smart-phones tested,
        // the default orientation is always "portrait", but in some Android and RIM based tablets,
        // the default orientation is "landscape". The following code attempts to use the window
        // dimensions to figure out what the current orientation is, and then makes adjustments
        // to the to the portrait_map if necessary, so that we can properly decode the
        // window.orientation value whenever get_orientation() is called.
        //
        // Note that we used to use a media query to figure out what the orientation the browser
        // thinks it is in:
        //
        //     initial_orientation_is_landscape = $.mobile.media("all and (orientation: landscape)");
        //
        // but there was an iPhone/iPod Touch bug beginning with iOS 4.2, up through iOS 5.1,
        // where the browser *ALWAYS* applied the landscape media query. This bug does not
        // happen on iPad.

        if ($.support.orientation) {

            // Check the window width and height to figure out what the current orientation
            // of the device is at this moment. Note that we've initialized the portrait map
            // values to 0 and 180, *AND* we purposely check for landscape so that if we guess
            // wrong, , we default to the assumption that portrait is the default orientation.
            // We use a threshold check below because on some platforms like iOS, the iPhone
            // form-factor can report a larger width than height if the user turns on the
            // developer console. The actual threshold value is somewhat arbitrary, we just
            // need to make sure it is large enough to exclude the developer console case.

            ww = window.innerWidth || win.width();
            wh = window.innerHeight || win.height();
            landscape_threshold = 50;

            initial_orientation_is_landscape = ww > wh && (ww - wh) > landscape_threshold;

            // Now check to see if the current window.orientation is 0 or 180.
            initial_orientation_is_default = portrait_map[window.orientation];

            // If the initial orientation is landscape, but window.orientation reports 0 or 180, *OR*
            // if the initial orientation is portrait, but window.orientation reports 90 or -90, we
            // need to flip our portrait_map values because landscape is the default orientation for
            // this device/browser.
            if ((initial_orientation_is_landscape && initial_orientation_is_default) || (!initial_orientation_is_landscape && !initial_orientation_is_default)) {
                portrait_map = { "-90": true, "90": true };
            }
        }

        $.event.special.orientationchange = $.extend({}, $.event.special.orientationchange, {
            setup: function () {
                // If the event is supported natively, return false so that jQuery
                // will bind to the event using DOM methods.
                if ($.support.orientation && !$.event.special.orientationchange.disabled) {
                    return false;
                }

                // Get the current orientation to avoid initial double-triggering.
                last_orientation = get_orientation();

                // Because the orientationchange event doesn't exist, simulate the
                // event by testing window dimensions on resize.
                win.bind("throttledresize", handler);
            },
            teardown: function () {
                // If the event is not supported natively, return false so that
                // jQuery will unbind the event using DOM methods.
                if ($.support.orientation && !$.event.special.orientationchange.disabled) {
                    return false;
                }

                // Because the orientationchange event doesn't exist, unbind the
                // resize event handler.
                win.unbind("throttledresize", handler);
            },
            add: function (handleObj) {
                // Save a reference to the bound event handler.
                var old_handler = handleObj.handler;

                handleObj.handler = function (event) {
                    // Modify event object, adding the .orientation property.
                    event.orientation = get_orientation();

                    // Call the originally-bound event handler and return its result.
                    return old_handler.apply(this, arguments);
                };
            }
        });

        // If the event is not supported natively, this handler will be bound to
        // the window resize event to simulate the orientationchange event.
        function handler() {
            // Get the current orientation.
            var orientation = get_orientation();

            if (orientation !== last_orientation) {
                // The orientation has changed, so trigger the orientationchange event.
                last_orientation = orientation;
                win.trigger(event_name);
            }
        }

        // Get the current page orientation. This method is exposed publicly, should it
        // be needed, as jQuery.event.special.orientationchange.orientation()
        $.event.special.orientationchange.orientation = get_orientation = function () {
            var isPortrait = true, elem = document.documentElement;

            // prefer window orientation to the calculation based on screensize as
            // the actual screen resize takes place before or after the orientation change event
            // has been fired depending on implementation (eg android 2.3 is before, iphone after).
            // More testing is required to determine if a more reliable method of determining the new screensize
            // is possible when orientationchange is fired. (eg, use media queries + element + opacity)
            if ($.support.orientation) {
                // if the window orientation registers as 0 or 180 degrees report
                // portrait, otherwise landscape
                isPortrait = portrait_map[window.orientation];
            } else {
                isPortrait = elem && elem.clientWidth / elem.clientHeight < 1.1;
            }

            return isPortrait ? "portrait" : "landscape";
        };

        $.fn[event_name] = function (fn) {
            return fn ? this.bind(event_name, fn) : this.trigger(event_name);
        };

        // jQuery < 1.8
        if ($.attrFn) {
            $.attrFn[event_name] = true;
        }

    }(jQuery, this));


    // This plugin is an experiment for abstracting away the touch and mouse
    // events so that developers don't have to worry about which method of input
    // the device their document is loaded on supports.
    //
    // The idea here is to allow the developer to register listeners for the
    // basic mouse events, such as mousedown, mousemove, mouseup, and click,
    // and the plugin will take care of registering the correct listeners
    // behind the scenes to invoke the listener at the fastest possible time
    // for that device, while still retaining the order of event firing in
    // the traditional mouse environment, should multiple handlers be registered
    // on the same element for different events.
    //
    // The current version exposes the following virtual events to jQuery bind methods:
    // "vmouseover vmousedown vmousemove vmouseup vclick vmouseout vmousecancel"

    (function ($, window, document, undefined) {

        var dataPropertyName = "virtualMouseBindings",
            touchTargetPropertyName = "virtualTouchID",
            virtualEventNames = "vmouseover vmousedown vmousemove vmouseup vclick vmouseout vmousecancel".split(" "),
            touchEventProps = "clientX clientY pageX pageY screenX screenY".split(" "),
            mouseHookProps = $.event.mouseHooks ? $.event.mouseHooks.props : [],
            mouseEventProps = $.event.props.concat(mouseHookProps),
            activeDocHandlers = {},
            resetTimerID = 0,
            startX = 0,
            startY = 0,
            didScroll = false,
            clickBlockList = [],
            blockMouseTriggers = false,
            blockTouchTriggers = false,
            eventCaptureSupported = "addEventListener" in document,
            $document = $(document),
            nextTouchID = 1,
            lastTouchID = 0, threshold,
            i;

        $.vmouse = {
            moveDistanceThreshold: 10,
            clickDistanceThreshold: 10,
            resetTimerDuration: 1500
        };

        function getNativeEvent(event) {

            while (event && typeof event.originalEvent !== "undefined") {
                event = event.originalEvent;
            }
            return event;
        }

        function createVirtualEvent(event, eventType) {

            var t = event.type,
                oe, props, ne, prop, ct, touch, i, j, len;

            event = $.Event(event);
            event.type = eventType;

            oe = event.originalEvent;
            props = $.event.props;

            // addresses separation of $.event.props in to $.event.mouseHook.props and Issue 3280
            // https://github.com/jquery/jquery-mobile/issues/3280
            if (t.search(/^(mouse|click)/) > -1) {
                props = mouseEventProps;
            }

            // copy original event properties over to the new event
            // this would happen if we could call $.event.fix instead of $.Event
            // but we don't have a way to force an event to be fixed multiple times
            if (oe) {
                for (i = props.length, prop; i;) {
                    prop = props[--i];
                    event[prop] = oe[prop];
                }
            }

            // make sure that if the mouse and click virtual events are generated
            // without a .which one is defined
            if (t.search(/mouse(down|up)|click/) > -1 && !event.which) {
                event.which = 1;
            }

            if (t.search(/^touch/) !== -1) {
                ne = getNativeEvent(oe);
                t = ne.touches;
                ct = ne.changedTouches;
                touch = (t && t.length) ? t[0] : ((ct && ct.length) ? ct[0] : undefined);

                if (touch) {
                    for (j = 0, len = touchEventProps.length; j < len; j++) {
                        prop = touchEventProps[j];
                        event[prop] = touch[prop];
                    }
                }
            }

            return event;
        }

        function getVirtualBindingFlags(element) {

            var flags = {},
                b, k;

            while (element) {

                b = $.data(element, dataPropertyName);

                for (k in b) {
                    if (b[k]) {
                        flags[k] = flags.hasVirtualBinding = true;
                    }
                }
                element = element.parentNode;
            }
            return flags;
        }

        function getClosestElementWithVirtualBinding(element, eventType) {
            var b;
            while (element) {

                b = $.data(element, dataPropertyName);

                if (b && (!eventType || b[eventType])) {
                    return element;
                }
                element = element.parentNode;
            }
            return null;
        }

        function enableTouchBindings() {
            blockTouchTriggers = false;
        }

        function disableTouchBindings() {
            blockTouchTriggers = true;
        }

        function enableMouseBindings() {
            lastTouchID = 0;
            clickBlockList.length = 0;
            blockMouseTriggers = false;

            // When mouse bindings are enabled, our
            // touch bindings are disabled.
            disableTouchBindings();
        }

        function disableMouseBindings() {
            // When mouse bindings are disabled, our
            // touch bindings are enabled.
            enableTouchBindings();
        }

        function startResetTimer() {
            clearResetTimer();
            resetTimerID = setTimeout(function () {
                resetTimerID = 0;
                enableMouseBindings();
            }, $.vmouse.resetTimerDuration);
        }

        function clearResetTimer() {
            if (resetTimerID) {
                clearTimeout(resetTimerID);
                resetTimerID = 0;
            }
        }

        function triggerVirtualEvent(eventType, event, flags) {
            var ve;

            if ((flags && flags[eventType]) ||
                        (!flags && getClosestElementWithVirtualBinding(event.target, eventType))) {

                ve = createVirtualEvent(event, eventType);

                $(event.target).trigger(ve);
            }

            return ve;
        }

        function mouseEventCallback(event) {
            var touchID = $.data(event.target, touchTargetPropertyName),
                ve;

            if (!blockMouseTriggers && (!lastTouchID || lastTouchID !== touchID)) {
                ve = triggerVirtualEvent("v" + event.type, event);
                if (ve) {
                    if (ve.isDefaultPrevented()) {
                        event.preventDefault();
                    }
                    if (ve.isPropagationStopped()) {
                        event.stopPropagation();
                    }
                    if (ve.isImmediatePropagationStopped()) {
                        event.stopImmediatePropagation();
                    }
                }
            }
        }

        function handleTouchStart(event) {

            var touches = getNativeEvent(event).touches,
                target, flags, t;

            if (touches && touches.length === 1) {

                target = event.target;
                flags = getVirtualBindingFlags(target);

                if (flags.hasVirtualBinding) {

                    lastTouchID = nextTouchID++;
                    $.data(target, touchTargetPropertyName, lastTouchID);

                    clearResetTimer();

                    disableMouseBindings();
                    didScroll = false;

                    t = getNativeEvent(event).touches[0];
                    startX = t.pageX;
                    startY = t.pageY;

                    triggerVirtualEvent("vmouseover", event, flags);
                    triggerVirtualEvent("vmousedown", event, flags);
                }
            }
        }

        function handleScroll(event) {
            if (blockTouchTriggers) {
                return;
            }

            if (!didScroll) {
                triggerVirtualEvent("vmousecancel", event, getVirtualBindingFlags(event.target));
            }

            didScroll = true;
            startResetTimer();
        }

        function handleTouchMove(event) {
            if (blockTouchTriggers) {
                return;
            }

            var t = getNativeEvent(event).touches[0],
                didCancel = didScroll,
                moveThreshold = $.vmouse.moveDistanceThreshold,
                flags = getVirtualBindingFlags(event.target);

            didScroll = didScroll ||
                (Math.abs(t.pageX - startX) > moveThreshold ||
                    Math.abs(t.pageY - startY) > moveThreshold);

            if (didScroll && !didCancel) {
                triggerVirtualEvent("vmousecancel", event, flags);
            }

            triggerVirtualEvent("vmousemove", event, flags);
            startResetTimer();
        }

        function handleTouchEnd(event) {
            if (blockTouchTriggers) {
                return;
            }

            disableTouchBindings();

            var flags = getVirtualBindingFlags(event.target),
                ve, t;
            triggerVirtualEvent("vmouseup", event, flags);

            if (!didScroll) {
                ve = triggerVirtualEvent("vclick", event, flags);
                if (ve && ve.isDefaultPrevented()) {
                    // The target of the mouse events that follow the touchend
                    // event don't necessarily match the target used during the
                    // touch. This means we need to rely on coordinates for blocking
                    // any click that is generated.
                    t = getNativeEvent(event).changedTouches[0];
                    clickBlockList.push({
                        touchID: lastTouchID,
                        x: t.clientX,
                        y: t.clientY
                    });

                    // Prevent any mouse events that follow from triggering
                    // virtual event notifications.
                    blockMouseTriggers = true;
                }
            }
            triggerVirtualEvent("vmouseout", event, flags);
            didScroll = false;

            startResetTimer();
        }

        function hasVirtualBindings(ele) {
            var bindings = $.data(ele, dataPropertyName),
                k;

            if (bindings) {
                for (k in bindings) {
                    if (bindings[k]) {
                        return true;
                    }
                }
            }
            return false;
        }

        function dummyMouseHandler() { }

        function getSpecialEventObject(eventType) {
            var realType = eventType.substr(1);

            return {
                setup: function (/* data, namespace */) {
                    // If this is the first virtual mouse binding for this element,
                    // add a bindings object to its data.

                    if (!hasVirtualBindings(this)) {
                        $.data(this, dataPropertyName, {});
                    }

                    // If setup is called, we know it is the first binding for this
                    // eventType, so initialize the count for the eventType to zero.
                    var bindings = $.data(this, dataPropertyName);
                    bindings[eventType] = true;

                    // If this is the first virtual mouse event for this type,
                    // register a global handler on the document.

                    activeDocHandlers[eventType] = (activeDocHandlers[eventType] || 0) + 1;

                    if (activeDocHandlers[eventType] === 1) {
                        $document.bind(realType, mouseEventCallback);
                    }

                    // Some browsers, like Opera Mini, won't dispatch mouse/click events
                    // for elements unless they actually have handlers registered on them.
                    // To get around this, we register dummy handlers on the elements.

                    $(this).bind(realType, dummyMouseHandler);

                    // For now, if event capture is not supported, we rely on mouse handlers.
                    if (eventCaptureSupported) {
                        // If this is the first virtual mouse binding for the document,
                        // register our touchstart handler on the document.

                        activeDocHandlers["touchstart"] = (activeDocHandlers["touchstart"] || 0) + 1;

                        if (activeDocHandlers["touchstart"] === 1) {
                            $document.bind("touchstart", handleTouchStart)
                                .bind("touchend", handleTouchEnd)

                                // On touch platforms, touching the screen and then dragging your finger
                                // causes the window content to scroll after some distance threshold is
                                // exceeded. On these platforms, a scroll prevents a click event from being
                                // dispatched, and on some platforms, even the touchend is suppressed. To
                                // mimic the suppression of the click event, we need to watch for a scroll
                                // event. Unfortunately, some platforms like iOS don't dispatch scroll
                                // events until *AFTER* the user lifts their finger (touchend). This means
                                // we need to watch both scroll and touchmove events to figure out whether
                                // or not a scroll happenens before the touchend event is fired.

                                .bind("touchmove", handleTouchMove)
                                .bind("scroll", handleScroll);
                        }
                    }
                },

                teardown: function (/* data, namespace */) {
                    // If this is the last virtual binding for this eventType,
                    // remove its global handler from the document.

                    --activeDocHandlers[eventType];

                    if (!activeDocHandlers[eventType]) {
                        $document.unbind(realType, mouseEventCallback);
                    }

                    if (eventCaptureSupported) {
                        // If this is the last virtual mouse binding in existence,
                        // remove our document touchstart listener.

                        --activeDocHandlers["touchstart"];

                        if (!activeDocHandlers["touchstart"]) {
                            $document.unbind("touchstart", handleTouchStart)
                                .unbind("touchmove", handleTouchMove)
                                .unbind("touchend", handleTouchEnd)
                                .unbind("scroll", handleScroll);
                        }
                    }

                    var $this = $(this),
                        bindings = $.data(this, dataPropertyName);

                    // teardown may be called when an element was
                    // removed from the DOM. If this is the case,
                    // jQuery core may have already stripped the element
                    // of any data bindings so we need to check it before
                    // using it.
                    if (bindings) {
                        bindings[eventType] = false;
                    }

                    // Unregister the dummy event handler.

                    $this.unbind(realType, dummyMouseHandler);

                    // If this is the last virtual mouse binding on the
                    // element, remove the binding data from the element.

                    if (!hasVirtualBindings(this)) {
                        $this.removeData(dataPropertyName);
                    }
                }
            };
        }

        // Expose our custom events to the jQuery bind/unbind mechanism.

        for (i = 0; i < virtualEventNames.length; i++) {
            $.event.special[virtualEventNames[i]] = getSpecialEventObject(virtualEventNames[i]);
        }

        // Add a capture click handler to block clicks.
        // Note that we require event capture support for this so if the device
        // doesn't support it, we punt for now and rely solely on mouse events.
        if (eventCaptureSupported) {
            document.addEventListener("click", function (e) {
                var cnt = clickBlockList.length,
                    target = e.target,
                    x, y, ele, i, o, touchID;

                if (cnt) {
                    x = e.clientX;
                    y = e.clientY;
                    threshold = $.vmouse.clickDistanceThreshold;

                    // The idea here is to run through the clickBlockList to see if
                    // the current click event is in the proximity of one of our
                    // vclick events that had preventDefault() called on it. If we find
                    // one, then we block the click.
                    //
                    // Why do we have to rely on proximity?
                    //
                    // Because the target of the touch event that triggered the vclick
                    // can be different from the target of the click event synthesized
                    // by the browser. The target of a mouse/click event that is synthesized
                    // from a touch event seems to be implementation specific. For example,
                    // some browsers will fire mouse/click events for a link that is near
                    // a touch event, even though the target of the touchstart/touchend event
                    // says the user touched outside the link. Also, it seems that with most
                    // browsers, the target of the mouse/click event is not calculated until the
                    // time it is dispatched, so if you replace an element that you touched
                    // with another element, the target of the mouse/click will be the new
                    // element underneath that point.
                    //
                    // Aside from proximity, we also check to see if the target and any
                    // of its ancestors were the ones that blocked a click. This is necessary
                    // because of the strange mouse/click target calculation done in the
                    // Android 2.1 browser, where if you click on an element, and there is a
                    // mouse/click handler on one of its ancestors, the target will be the
                    // innermost child of the touched element, even if that child is no where
                    // near the point of touch.

                    ele = target;

                    while (ele) {
                        for (i = 0; i < cnt; i++) {
                            o = clickBlockList[i];
                            touchID = 0;

                            if ((ele === target && Math.abs(o.x - x) < threshold && Math.abs(o.y - y) < threshold) ||
                                        $.data(ele, touchTargetPropertyName) === o.touchID) {
                                // XXX: We may want to consider removing matches from the block list
                                //      instead of waiting for the reset timer to fire.
                                e.preventDefault();
                                e.stopPropagation();
                                return;
                            }
                        }
                        ele = ele.parentNode;
                    }
                }
            }, true);
        }
    })(jQuery, window, document);


    (function ($, window, undefined) {
        var $document = $(document),
            supportTouch = $.mobile.support.touch,
            scrollEvent = "touchmove scroll",
            touchStartEvent = supportTouch ? "touchstart" : "mousedown",
            touchStopEvent = supportTouch ? "touchend" : "mouseup",
            touchMoveEvent = supportTouch ? "touchmove" : "mousemove";

        // setup new event shortcuts
        $.each(("touchstart touchmove touchend " +
            "tap taphold " +
            "swipe swipeleft swiperight " +
            "scrollstart scrollstop").split(" "), function (i, name) {

                $.fn[name] = function (fn) {
                    return fn ? this.bind(name, fn) : this.trigger(name);
                };

                // jQuery < 1.8
                if ($.attrFn) {
                    $.attrFn[name] = true;
                }
            });

        function triggerCustomEvent(obj, eventType, event, bubble) {
            var originalType = event.type;
            event.type = eventType;
            if (bubble) {
                $.event.trigger(event, undefined, obj);
            } else {
                $.event.dispatch.call(obj, event);
            }
            event.type = originalType;
        }

        // also handles scrollstop
        $.event.special.scrollstart = {

            enabled: true,
            setup: function () {

                var thisObject = this,
                    $this = $(thisObject),
                    scrolling,
                    timer;

                function trigger(event, state) {
                    scrolling = state;
                    triggerCustomEvent(thisObject, scrolling ? "scrollstart" : "scrollstop", event);
                }

                // iPhone triggers scroll after a small delay; use touchmove instead
                $this.bind(scrollEvent, function (event) {

                    if (!$.event.special.scrollstart.enabled) {
                        return;
                    }

                    if (!scrolling) {
                        trigger(event, true);
                    }

                    clearTimeout(timer);
                    timer = setTimeout(function () {
                        trigger(event, false);
                    }, 50);
                });
            },
            teardown: function () {
                $(this).unbind(scrollEvent);
            }
        };

        // also handles taphold
        $.event.special.tap = {
            tapholdThreshold: 750,
            emitTapOnTaphold: true,
            setup: function () {
                var thisObject = this,
                    $this = $(thisObject),
                    isTaphold = false;

                $this.bind("vmousedown", function (event) {
                    isTaphold = false;
                    if (event.which && event.which !== 1) {
                        return false;
                    }

                    var origTarget = event.target,
                        timer;

                    function clearTapTimer() {
                        clearTimeout(timer);
                    }

                    function clearTapHandlers() {
                        clearTapTimer();

                        $this.unbind("vclick", clickHandler)
                            .unbind("vmouseup", clearTapTimer);
                        $document.unbind("vmousecancel", clearTapHandlers);
                    }

                    function clickHandler(event) {
                        clearTapHandlers();

                        // ONLY trigger a 'tap' event if the start target is
                        // the same as the stop target.
                        if (!isTaphold && origTarget === event.target) {
                            triggerCustomEvent(thisObject, "tap", event);
                        } else if (isTaphold) {
                            event.preventDefault();
                        }
                    }

                    $this.bind("vmouseup", clearTapTimer)
                        .bind("vclick", clickHandler);
                    $document.bind("vmousecancel", clearTapHandlers);

                    timer = setTimeout(function () {
                        if (!$.event.special.tap.emitTapOnTaphold) {
                            isTaphold = true;
                        }
                        triggerCustomEvent(thisObject, "taphold", $.Event("taphold", { target: origTarget }));
                    }, $.event.special.tap.tapholdThreshold);
                });
            },
            teardown: function () {
                $(this).unbind("vmousedown").unbind("vclick").unbind("vmouseup");
                $document.unbind("vmousecancel");
            }
        };

        // Also handles swipeleft, swiperight
        $.event.special.swipe = {

            // More than this horizontal displacement, and we will suppress scrolling.
            scrollSupressionThreshold: 30,

            // More time than this, and it isn't a swipe.
            durationThreshold: 1000,

            // Swipe horizontal displacement must be more than this.
            horizontalDistanceThreshold: 30,

            // Swipe vertical displacement must be less than this.
            verticalDistanceThreshold: 30,

            getLocation: function (event) {
                var winPageX = window.pageXOffset,
                    winPageY = window.pageYOffset,
                    x = event.clientX,
                    y = event.clientY;

                if (event.pageY === 0 && Math.floor(y) > Math.floor(event.pageY) ||
                    event.pageX === 0 && Math.floor(x) > Math.floor(event.pageX)) {

                    // iOS4 clientX/clientY have the value that should have been
                    // in pageX/pageY. While pageX/page/ have the value 0
                    x = x - winPageX;
                    y = y - winPageY;
                } else if (y < (event.pageY - winPageY) || x < (event.pageX - winPageX)) {

                    // Some Android browsers have totally bogus values for clientX/Y
                    // when scrolling/zooming a page. Detectable since clientX/clientY
                    // should never be smaller than pageX/pageY minus page scroll
                    x = event.pageX - winPageX;
                    y = event.pageY - winPageY;
                }

                return {
                    x: x,
                    y: y
                };
            },

            start: function (event) {
                var data = event.originalEvent.touches ?
                        event.originalEvent.touches[0] : event,
                    location = $.event.special.swipe.getLocation(data);
                return {
                    time: (new Date()).getTime(),
                    coords: [location.x, location.y],
                    origin: $(event.target)
                };
            },

            stop: function (event) {
                var data = event.originalEvent.touches ?
                        event.originalEvent.touches[0] : event,
                    location = $.event.special.swipe.getLocation(data);
                return {
                    time: (new Date()).getTime(),
                    coords: [location.x, location.y]
                };
            },

            handleSwipe: function (start, stop, thisObject, origTarget) {
                if (stop.time - start.time < $.event.special.swipe.durationThreshold &&
                    Math.abs(start.coords[0] - stop.coords[0]) > $.event.special.swipe.horizontalDistanceThreshold &&
                    Math.abs(start.coords[1] - stop.coords[1]) < $.event.special.swipe.verticalDistanceThreshold) {
                    var direction = start.coords[0] > stop.coords[0] ? "swipeleft" : "swiperight";

                    triggerCustomEvent(thisObject, "swipe", $.Event("swipe", { target: origTarget, swipestart: start, swipestop: stop }), true);
                    triggerCustomEvent(thisObject, direction, $.Event(direction, { target: origTarget, swipestart: start, swipestop: stop }), true);
                    return true;
                }
                return false;

            },

            // This serves as a flag to ensure that at most one swipe event event is
            // in work at any given time
            eventInProgress: false,

            setup: function () {
                var events,
                    thisObject = this,
                    $this = $(thisObject),
                    context = {};

                // Retrieve the events data for this element and add the swipe context
                events = $.data(this, "mobile-events");
                if (!events) {
                    events = { length: 0 };
                    $.data(this, "mobile-events", events);
                }
                events.length++;
                events.swipe = context;

                context.start = function (event) {

                    // Bail if we're already working on a swipe event
                    if ($.event.special.swipe.eventInProgress) {
                        return;
                    }
                    $.event.special.swipe.eventInProgress = true;

                    var stop,
                        start = $.event.special.swipe.start(event),
                        origTarget = event.target,
                        emitted = false;

                    context.move = function (event) {
                        if (!start || event.isDefaultPrevented()) {
                            return;
                        }

                        stop = $.event.special.swipe.stop(event);
                        if (!emitted) {
                            emitted = $.event.special.swipe.handleSwipe(start, stop, thisObject, origTarget);
                            if (emitted) {

                                // Reset the context to make way for the next swipe event
                                $.event.special.swipe.eventInProgress = false;
                            }
                        }
                        // prevent scrolling
                        if (Math.abs(start.coords[0] - stop.coords[0]) > $.event.special.swipe.scrollSupressionThreshold) {
                            event.preventDefault();
                        }
                    };

                    context.stop = function () {
                        emitted = true;

                        // Reset the context to make way for the next swipe event
                        $.event.special.swipe.eventInProgress = false;
                        $document.off(touchMoveEvent, context.move);
                        context.move = null;
                    };

                    $document.on(touchMoveEvent, context.move)
                        .one(touchStopEvent, context.stop);
                };
                $this.on(touchStartEvent, context.start);
            },

            teardown: function () {
                var events, context;

                events = $.data(this, "mobile-events");
                if (events) {
                    context = events.swipe;
                    delete events.swipe;
                    events.length--;
                    if (events.length === 0) {
                        $.removeData(this, "mobile-events");
                    }
                }

                if (context) {
                    if (context.start) {
                        $(this).off(touchStartEvent, context.start);
                    }
                    if (context.move) {
                        $document.off(touchMoveEvent, context.move);
                    }
                    if (context.stop) {
                        $document.off(touchStopEvent, context.stop);
                    }
                }
            }
        };
        $.each({
            scrollstop: "scrollstart",
            taphold: "tap",
            swipeleft: "swipe.left",
            swiperight: "swipe.right"
        }, function (event, sourceEvent) {

            $.event.special[event] = {
                setup: function () {
                    $(this).bind(sourceEvent, $.noop);
                },
                teardown: function () {
                    $(this).unbind(sourceEvent);
                }
            };
        });

    })(jQuery, this);

    (function ($, undefined) {
        var props = {
            "animation": {},
            "transition": {}
        },
            testElement = document.createElement("a"),
            vendorPrefixes = ["", "webkit-", "moz-", "o-"];

        $.each(["animation", "transition"], function (i, test) {

            // Get correct name for test
            var testName = (i === 0) ? test + "-" + "name" : test;

            $.each(vendorPrefixes, function (j, prefix) {
                if (testElement.style[$.camelCase(prefix + testName)] !== undefined) {
                    props[test]["prefix"] = prefix;
                    return false;
                }
            });

            // Set event and duration names for later use
            props[test]["duration"] =
                $.camelCase(props[test]["prefix"] + test + "-" + "duration");
            props[test]["event"] =
                $.camelCase(props[test]["prefix"] + test + "-" + "end");

            // All lower case if not a vendor prop
            if (props[test]["prefix"] === "") {
                props[test]["event"] = props[test]["event"].toLowerCase();
            }
        });

        // If a valid prefix was found then the it is supported by the browser
        $.support.cssTransitions = (props["transition"]["prefix"] !== undefined);
        $.support.cssAnimations = (props["animation"]["prefix"] !== undefined);

        // Remove the testElement
        $(testElement).remove();

        // Animation complete callback
        $.fn.animationComplete = function (callback, type, fallbackTime) {
            var timer, duration,
                that = this,
                eventBinding = function () {

                    // Clear the timer so we don't call callback twice
                    clearTimeout(timer);
                    callback.apply(this, arguments);
                },
                animationType = (!type || type === "animation") ? "animation" : "transition";

            // Make sure selected type is supported by browser
            if (($.support.cssTransitions && animationType === "transition") ||
                ($.support.cssAnimations && animationType === "animation")) {

                // If a fallback time was not passed set one
                if (fallbackTime === undefined) {

                    // Make sure the was not bound to document before checking .css
                    if ($(this).context !== document) {

                        // Parse the durration since its in second multiple by 1000 for milliseconds
                        // Multiply by 3 to make sure we give the animation plenty of time.
                        duration = parseFloat(
                            $(this).css(props[animationType].duration)
                        ) * 3000;
                    }

                    // If we could not read a duration use the default
                    if (duration === 0 || duration === undefined || isNaN(duration)) {
                        duration = $.fn.animationComplete.defaultDuration;
                    }
                }

                // Sets up the fallback if event never comes
                timer = setTimeout(function () {
                    $(that).off(props[animationType].event, eventBinding);
                    callback.apply(that);
                }, duration);

                // Bind the event
                return $(this).one(props[animationType].event, eventBinding);
            } else {

                // CSS animation / transitions not supported
                // Defer execution for consistency between webkit/non webkit
                setTimeout($.proxy(callback, this), 0);
                return $(this);
            }
        };

        // Allow default callback to be configured on mobileInit
        $.fn.animationComplete.defaultDuration = 1000;
    })(jQuery);


    // buttonMarkup is deprecated as of 1.4.0 and will be removed in 1.5.0.

    (function ($, undefined) {

        // General policy: Do not access data-* attributes except during enhancement.
        // In all other cases we determine the state of the button exclusively from its
        // className. That's why optionsToClasses expects a full complement of options,
        // and the jQuery plugin completes the set of options from the default values.

        // Map classes to buttonMarkup boolean options - used in classNameToOptions()
        var reverseBoolOptionMap = {
            "ui-shadow": "shadow",
            "ui-corner-all": "corners",
            "ui-btn-inline": "inline",
            "ui-shadow-icon": "iconshadow", /* TODO: Remove in 1.5 */
            "ui-mini": "mini"
        },
            getAttrFixed = function () {
                var ret = $.mobile.getAttribute.apply(this, arguments);

                return (ret == null ? undefined : ret);
            },
            capitalLettersRE = /[A-Z]/g;

        // optionsToClasses:
        // @options: A complete set of options to convert to class names.
        // @existingClasses: extra classes to add to the result
        //
        // Converts @options to buttonMarkup classes and returns the result as an array
        // that can be converted to an element's className with .join( " " ). All
        // possible options must be set inside @options. Use $.fn.buttonMarkup.defaults
        // to get a complete set and use $.extend to override your choice of options
        // from that set.
        function optionsToClasses(options, existingClasses) {
            var classes = existingClasses ? existingClasses : [];

            // Add classes to the array - first ui-btn
            classes.push("ui-btn");

            // If there is a theme
            if (options.theme) {
                classes.push("ui-btn-" + options.theme);
            }

            // If there's an icon, add the icon-related classes
            if (options.icon) {
                classes = classes.concat([
                    "ui-icon-" + options.icon,
                    "ui-btn-icon-" + options.iconpos
                ]);
                if (options.iconshadow) {
                    classes.push("ui-shadow-icon"); /* TODO: Remove in 1.5 */
                }
            }

            // Add the appropriate class for each boolean option
            if (options.inline) {
                classes.push("ui-btn-inline");
            }
            if (options.shadow) {
                classes.push("ui-shadow");
            }
            if (options.corners) {
                classes.push("ui-corner-all");
            }
            if (options.mini) {
                classes.push("ui-mini");
            }

            // Create a string from the array and return it
            return classes;
        }

        // classNameToOptions:
        // @classes: A string containing a .className-style space-separated class list
        //
        // Loops over @classes and calculates an options object based on the
        // buttonMarkup-related classes it finds. It records unrecognized classes in an
        // array.
        //
        // Returns: An object containing the following items:
        //
        // "options": buttonMarkup options found to be present because of the
        // presence/absence of corresponding classes
        //
        // "unknownClasses": a string containing all the non-buttonMarkup-related
        // classes found in @classes
        //
        // "alreadyEnhanced": A boolean indicating whether the ui-btn class was among
        // those found to be present
        function classNameToOptions(classes) {
            var idx, map, unknownClass,
                alreadyEnhanced = false,
                noIcon = true,
                o = {
                    icon: "",
                    inline: false,
                    shadow: false,
                    corners: false,
                    iconshadow: false,
                    mini: false
                },
                unknownClasses = [];

            classes = classes.split(" ");

            // Loop over the classes
            for (idx = 0 ; idx < classes.length ; idx++) {

                // Assume it's an unrecognized class
                unknownClass = true;

                // Recognize boolean options from the presence of classes
                map = reverseBoolOptionMap[classes[idx]];
                if (map !== undefined) {
                    unknownClass = false;
                    o[map] = true;

                    // Recognize the presence of an icon and establish the icon position
                } else if (classes[idx].indexOf("ui-btn-icon-") === 0) {
                    unknownClass = false;
                    noIcon = false;
                    o.iconpos = classes[idx].substring(12);

                    // Establish which icon is present
                } else if (classes[idx].indexOf("ui-icon-") === 0) {
                    unknownClass = false;
                    o.icon = classes[idx].substring(8);

                    // Establish the theme - this recognizes one-letter theme swatch names
                } else if (classes[idx].indexOf("ui-btn-") === 0 && classes[idx].length === 8) {
                    unknownClass = false;
                    o.theme = classes[idx].substring(7);

                    // Recognize that this element has already been buttonMarkup-enhanced
                } else if (classes[idx] === "ui-btn") {
                    unknownClass = false;
                    alreadyEnhanced = true;
                }

                // If this class has not been recognized, add it to the list
                if (unknownClass) {
                    unknownClasses.push(classes[idx]);
                }
            }

            // If a "ui-btn-icon-*" icon position class is absent there cannot be an icon
            if (noIcon) {
                o.icon = "";
            }

            return {
                options: o,
                unknownClasses: unknownClasses,
                alreadyEnhanced: alreadyEnhanced
            };
        }

        function camelCase2Hyphenated(c) {
            return "-" + c.toLowerCase();
        }

        // $.fn.buttonMarkup:
        // DOM: gets/sets .className
        //
        // @options: options to apply to the elements in the jQuery object
        // @overwriteClasses: boolean indicating whether to honour existing classes
        //
        // Calculates the classes to apply to the elements in the jQuery object based on
        // the options passed in. If @overwriteClasses is true, it sets the className
        // property of each element in the jQuery object to the buttonMarkup classes
        // it calculates based on the options passed in.
        //
        // If you wish to preserve any classes that are already present on the elements
        // inside the jQuery object, including buttonMarkup-related classes that were
        // added by a previous call to $.fn.buttonMarkup() or during page enhancement
        // then you should omit @overwriteClasses or set it to false.
        $.fn.buttonMarkup = function (options, overwriteClasses) {
            var idx, data, el, retrievedOptions, optionKey,
                defaults = $.fn.buttonMarkup.defaults;

            for (idx = 0 ; idx < this.length ; idx++) {
                el = this[idx];
                data = overwriteClasses ?

                    // Assume this element is not enhanced and ignore its classes
			{ alreadyEnhanced: false, unknownClasses: [] } :

                    // Otherwise analyze existing classes to establish existing options and
                    // classes
                    classNameToOptions(el.className);

                retrievedOptions = $.extend({},

                    // If the element already has the class ui-btn, then we assume that
                    // it has passed through buttonMarkup before - otherwise, the options
                    // returned by classNameToOptions do not correctly reflect the state of
                    // the element
                    (data.alreadyEnhanced ? data.options : {}),

                    // Finally, apply the options passed in
                    options);

                // If this is the first call on this element, retrieve remaining options
                // from the data-attributes
                if (!data.alreadyEnhanced) {
                    for (optionKey in defaults) {
                        if (retrievedOptions[optionKey] === undefined) {
                            retrievedOptions[optionKey] = getAttrFixed(el,
                                optionKey.replace(capitalLettersRE, camelCase2Hyphenated)
                            );
                        }
                    }
                }

                el.className = optionsToClasses(

                    // Merge all the options and apply them as classes
                    $.extend({},

                        // The defaults form the basis
                        defaults,

                        // Add the computed options
                        retrievedOptions
                    ),

                    // ... and re-apply any unrecognized classes that were found
                    data.unknownClasses).join(" ");
                if (el.tagName.toLowerCase() !== "button") {
                    el.setAttribute("role", "button");
                }
            }

            return this;
        };

        // buttonMarkup defaults. This must be a complete set, i.e., a value must be
        // given here for all recognized options
        $.fn.buttonMarkup.defaults = {
            icon: "",
            iconpos: "left",
            theme: null,
            inline: false,
            shadow: true,
            corners: true,
            iconshadow: false, /* TODO: Remove in 1.5. Option deprecated in 1.4. */
            mini: false
        };

        $.extend($.fn.buttonMarkup, {
            initSelector: "a:jqmData(role='button'), .ui-bar > a, .ui-bar > :jqmData(role='controlgroup') > a, button:not(:jqmData(role='navbar') button)"
        });

    })(jQuery);


    /*!
     * jQuery UI Widget c0ab71056b936627e8a7821f03c044aec6280a40
     * http://jqueryui.com
     *
     * Copyright 2013 jQuery Foundation and other contributors
     * Released under the MIT license.
     * http://jquery.org/license
     *
     * http://api.jqueryui.com/jQuery.widget/
     */
    (function ($, undefined) {

        var uuid = 0,
            slice = Array.prototype.slice,
            _cleanData = $.cleanData;
        $.cleanData = function (elems) {
            for (var i = 0, elem; (elem = elems[i]) != null; i++) {
                try {
                    $(elem).triggerHandler("remove");
                    // http://bugs.jquery.com/ticket/8235
                } catch (e) { }
            }
            _cleanData(elems);
        };

        $.widget = function (name, base, prototype) {
            var fullName, existingConstructor, constructor, basePrototype,
                // proxiedPrototype allows the provided prototype to remain unmodified
                // so that it can be used as a mixin for multiple widgets (#8876)
                proxiedPrototype = {},
                namespace = name.split(".")[0];

            name = name.split(".")[1];
            fullName = namespace + "-" + name;

            if (!prototype) {
                prototype = base;
                base = $.Widget;
            }

            // create selector for plugin
            $.expr[":"][fullName.toLowerCase()] = function (elem) {
                return !!$.data(elem, fullName);
            };

            $[namespace] = $[namespace] || {};
            existingConstructor = $[namespace][name];
            constructor = $[namespace][name] = function (options, element) {
                // allow instantiation without "new" keyword
                if (!this._createWidget) {
                    return new constructor(options, element);
                }

                // allow instantiation without initializing for simple inheritance
                // must use "new" keyword (the code above always passes args)
                if (arguments.length) {
                    this._createWidget(options, element);
                }
            };
            // extend with the existing constructor to carry over any static properties
            $.extend(constructor, existingConstructor, {
                version: prototype.version,
                // copy the object used to create the prototype in case we need to
                // redefine the widget later
                _proto: $.extend({}, prototype),
                // track widgets that inherit from this widget in case this widget is
                // redefined after a widget inherits from it
                _childConstructors: []
            });

            basePrototype = new base();
            // we need to make the options hash a property directly on the new instance
            // otherwise we'll modify the options hash on the prototype that we're
            // inheriting from
            basePrototype.options = $.widget.extend({}, basePrototype.options);
            $.each(prototype, function (prop, value) {
                if (!$.isFunction(value)) {
                    proxiedPrototype[prop] = value;
                    return;
                }
                proxiedPrototype[prop] = (function () {
                    var _super = function () {
                        return base.prototype[prop].apply(this, arguments);
                    },
                        _superApply = function (args) {
                            return base.prototype[prop].apply(this, args);
                        };
                    return function () {
                        var __super = this._super,
                            __superApply = this._superApply,
                            returnValue;

                        this._super = _super;
                        this._superApply = _superApply;

                        returnValue = value.apply(this, arguments);

                        this._super = __super;
                        this._superApply = __superApply;

                        return returnValue;
                    };
                })();
            });
            constructor.prototype = $.widget.extend(basePrototype, {
                // TODO: remove support for widgetEventPrefix
                // always use the name + a colon as the prefix, e.g., draggable:start
                // don't prefix for widgets that aren't DOM-based
                widgetEventPrefix: existingConstructor ? (basePrototype.widgetEventPrefix || name) : name
            }, proxiedPrototype, {
                constructor: constructor,
                namespace: namespace,
                widgetName: name,
                widgetFullName: fullName
            });

            // If this widget is being redefined then we need to find all widgets that
            // are inheriting from it and redefine all of them so that they inherit from
            // the new version of this widget. We're essentially trying to replace one
            // level in the prototype chain.
            if (existingConstructor) {
                $.each(existingConstructor._childConstructors, function (i, child) {
                    var childPrototype = child.prototype;

                    // redefine the child widget using the same prototype that was
                    // originally used, but inherit from the new version of the base
                    $.widget(childPrototype.namespace + "." + childPrototype.widgetName, constructor, child._proto);
                });
                // remove the list of existing child constructors from the old constructor
                // so the old child constructors can be garbage collected
                delete existingConstructor._childConstructors;
            } else {
                base._childConstructors.push(constructor);
            }

            $.widget.bridge(name, constructor);

            return constructor;
        };

        $.widget.extend = function (target) {
            var input = slice.call(arguments, 1),
                inputIndex = 0,
                inputLength = input.length,
                key,
                value;
            for (; inputIndex < inputLength; inputIndex++) {
                for (key in input[inputIndex]) {
                    value = input[inputIndex][key];
                    if (input[inputIndex].hasOwnProperty(key) && value !== undefined) {
                        // Clone objects
                        if ($.isPlainObject(value)) {
                            target[key] = $.isPlainObject(target[key]) ?
                                $.widget.extend({}, target[key], value) :
                                // Don't extend strings, arrays, etc. with objects
                                $.widget.extend({}, value);
                            // Copy everything else by reference
                        } else {
                            target[key] = value;
                        }
                    }
                }
            }
            return target;
        };

        $.widget.bridge = function (name, object) {
            var fullName = object.prototype.widgetFullName || name;
            $.fn[name] = function (options) {
                var isMethodCall = typeof options === "string",
                    args = slice.call(arguments, 1),
                    returnValue = this;

                // allow multiple hashes to be passed on init
                options = !isMethodCall && args.length ?
                    $.widget.extend.apply(null, [options].concat(args)) :
                    options;

                if (isMethodCall) {
                    this.each(function () {
                        var methodValue,
                            instance = $.data(this, fullName);
                        if (options === "instance") {
                            returnValue = instance;
                            return false;
                        }
                        if (!instance) {
                            return $.error("cannot call methods on " + name + " prior to initialization; " +
                                "attempted to call method '" + options + "'");
                        }
                        if (!$.isFunction(instance[options]) || options.charAt(0) === "_") {
                            return $.error("no such method '" + options + "' for " + name + " widget instance");
                        }
                        methodValue = instance[options].apply(instance, args);
                        if (methodValue !== instance && methodValue !== undefined) {
                            returnValue = methodValue && methodValue.jquery ?
                                returnValue.pushStack(methodValue.get()) :
                                methodValue;
                            return false;
                        }
                    });
                } else {
                    this.each(function () {
                        var instance = $.data(this, fullName);
                        if (instance) {
                            instance.option(options || {})._init();
                        } else {
                            $.data(this, fullName, new object(options, this));
                        }
                    });
                }

                return returnValue;
            };
        };

        $.Widget = function ( /* options, element */) { };
        $.Widget._childConstructors = [];

        $.Widget.prototype = {
            widgetName: "widget",
            widgetEventPrefix: "",
            defaultElement: "<div>",
            options: {
                disabled: false,

                // callbacks
                create: null
            },
            _createWidget: function (options, element) {
                element = $(element || this.defaultElement || this)[0];
                this.element = $(element);
                this.uuid = uuid++;
                this.eventNamespace = "." + this.widgetName + this.uuid;
                this.options = $.widget.extend({},
                    this.options,
                    this._getCreateOptions(),
                    options);

                this.bindings = $();
                this.hoverable = $();
                this.focusable = $();

                if (element !== this) {
                    $.data(element, this.widgetFullName, this);
                    this._on(true, this.element, {
                        remove: function (event) {
                            if (event.target === element) {
                                this.destroy();
                            }
                        }
                    });
                    this.document = $(element.style ?
                        // element within the document
                        element.ownerDocument :
                        // element is window or document
                        element.document || element);
                    this.window = $(this.document[0].defaultView || this.document[0].parentWindow);
                }

                this._create();
                this._trigger("create", null, this._getCreateEventData());
                this._init();
            },
            _getCreateOptions: $.noop,
            _getCreateEventData: $.noop,
            _create: $.noop,
            _init: $.noop,

            destroy: function () {
                this._destroy();
                // we can probably remove the unbind calls in 2.0
                // all event bindings should go through this._on()
                this.element
                    .unbind(this.eventNamespace)
                    .removeData(this.widgetFullName)
                    // support: jquery <1.6.3
                    // http://bugs.jquery.com/ticket/9413
                    .removeData($.camelCase(this.widgetFullName));
                this.widget()
                    .unbind(this.eventNamespace)
                    .removeAttr("aria-disabled")
                    .removeClass(
                        this.widgetFullName + "-disabled " +
                        "ui-state-disabled");

                // clean up events and states
                this.bindings.unbind(this.eventNamespace);
                this.hoverable.removeClass("ui-state-hover");
                this.focusable.removeClass("ui-state-focus");
            },
            _destroy: $.noop,

            widget: function () {
                return this.element;
            },

            option: function (key, value) {
                var options = key,
                    parts,
                    curOption,
                    i;

                if (arguments.length === 0) {
                    // don't return a reference to the internal hash
                    return $.widget.extend({}, this.options);
                }

                if (typeof key === "string") {
                    // handle nested keys, e.g., "foo.bar" => { foo: { bar: ___ } }
                    options = {};
                    parts = key.split(".");
                    key = parts.shift();
                    if (parts.length) {
                        curOption = options[key] = $.widget.extend({}, this.options[key]);
                        for (i = 0; i < parts.length - 1; i++) {
                            curOption[parts[i]] = curOption[parts[i]] || {};
                            curOption = curOption[parts[i]];
                        }
                        key = parts.pop();
                        if (value === undefined) {
                            return curOption[key] === undefined ? null : curOption[key];
                        }
                        curOption[key] = value;
                    } else {
                        if (value === undefined) {
                            return this.options[key] === undefined ? null : this.options[key];
                        }
                        options[key] = value;
                    }
                }

                this._setOptions(options);

                return this;
            },
            _setOptions: function (options) {
                var key;

                for (key in options) {
                    this._setOption(key, options[key]);
                }

                return this;
            },
            _setOption: function (key, value) {
                this.options[key] = value;

                if (key === "disabled") {
                    this.widget()
                        .toggleClass(this.widgetFullName + "-disabled", !!value);
                    this.hoverable.removeClass("ui-state-hover");
                    this.focusable.removeClass("ui-state-focus");
                }

                return this;
            },

            enable: function () {
                return this._setOptions({ disabled: false });
            },
            disable: function () {
                return this._setOptions({ disabled: true });
            },

            _on: function (suppressDisabledCheck, element, handlers) {
                var delegateElement,
                    instance = this;

                // no suppressDisabledCheck flag, shuffle arguments
                if (typeof suppressDisabledCheck !== "boolean") {
                    handlers = element;
                    element = suppressDisabledCheck;
                    suppressDisabledCheck = false;
                }

                // no element argument, shuffle and use this.element
                if (!handlers) {
                    handlers = element;
                    element = this.element;
                    delegateElement = this.widget();
                } else {
                    // accept selectors, DOM elements
                    element = delegateElement = $(element);
                    this.bindings = this.bindings.add(element);
                }

                $.each(handlers, function (event, handler) {
                    function handlerProxy() {
                        // allow widgets to customize the disabled handling
                        // - disabled as an array instead of boolean
                        // - disabled class as method for disabling individual parts
                        if (!suppressDisabledCheck &&
                                (instance.options.disabled === true ||
                                    $(this).hasClass("ui-state-disabled"))) {
                            return;
                        }
                        return (typeof handler === "string" ? instance[handler] : handler)
                            .apply(instance, arguments);
                    }

                    // copy the guid so direct unbinding works
                    if (typeof handler !== "string") {
                        handlerProxy.guid = handler.guid =
                            handler.guid || handlerProxy.guid || $.guid++;
                    }

                    var match = event.match(/^(\w+)\s*(.*)$/),
                        eventName = match[1] + instance.eventNamespace,
                        selector = match[2];
                    if (selector) {
                        delegateElement.delegate(selector, eventName, handlerProxy);
                    } else {
                        element.bind(eventName, handlerProxy);
                    }
                });
            },

            _off: function (element, eventName) {
                eventName = (eventName || "").split(" ").join(this.eventNamespace + " ") + this.eventNamespace;
                element.unbind(eventName).undelegate(eventName);
            },

            _delay: function (handler, delay) {
                function handlerProxy() {
                    return (typeof handler === "string" ? instance[handler] : handler)
                        .apply(instance, arguments);
                }
                var instance = this;
                return setTimeout(handlerProxy, delay || 0);
            },

            _hoverable: function (element) {
                this.hoverable = this.hoverable.add(element);
                this._on(element, {
                    mouseenter: function (event) {
                        $(event.currentTarget).addClass("ui-state-hover");
                    },
                    mouseleave: function (event) {
                        $(event.currentTarget).removeClass("ui-state-hover");
                    }
                });
            },

            _focusable: function (element) {
                this.focusable = this.focusable.add(element);
                this._on(element, {
                    focusin: function (event) {
                        $(event.currentTarget).addClass("ui-state-focus");
                    },
                    focusout: function (event) {
                        $(event.currentTarget).removeClass("ui-state-focus");
                    }
                });
            },

            _trigger: function (type, event, data) {
                var prop, orig,
                    callback = this.options[type];

                data = data || {};
                event = $.Event(event);
                event.type = (type === this.widgetEventPrefix ?
                    type :
                    this.widgetEventPrefix + type).toLowerCase();
                // the original event may come from any element
                // so we need to reset the target on the new event
                event.target = this.element[0];

                // copy original event properties over to the new event
                orig = event.originalEvent;
                if (orig) {
                    for (prop in orig) {
                        if (!(prop in event)) {
                            event[prop] = orig[prop];
                        }
                    }
                }

                this.element.trigger(event, data);
                return !($.isFunction(callback) &&
                    callback.apply(this.element[0], [event].concat(data)) === false ||
                    event.isDefaultPrevented());
            }
        };

        $.each({ show: "fadeIn", hide: "fadeOut" }, function (method, defaultEffect) {
            $.Widget.prototype["_" + method] = function (element, options, callback) {
                if (typeof options === "string") {
                    options = { effect: options };
                }
                var hasOptions,
                    effectName = !options ?
                    method :
                        options === true || typeof options === "number" ?
                    defaultEffect :
                            options.effect || defaultEffect;
                options = options || {};
                if (typeof options === "number") {
                    options = { duration: options };
                }
                hasOptions = !$.isEmptyObject(options);
                options.complete = callback;
                if (options.delay) {
                    element.delay(options.delay);
                }
                if (hasOptions && $.effects && $.effects.effect[effectName]) {
                    element[method](options);
                } else if (effectName !== method && element[effectName]) {
                    element[effectName](options.duration, options.easing, callback);
                } else {
                    element.queue(function (next) {
                        $(this)[method]();
                        if (callback) {
                            callback.call(element[0]);
                        }
                        next();
                    });
                }
            };
        });

    })(jQuery);

    (function ($, undefined) {

        var rcapitals = /[A-Z]/g,
            replaceFunction = function (c) {
                return "-" + c.toLowerCase();
            };

        $.extend($.Widget.prototype, {
            _getCreateOptions: function () {
                var option, value,
                    elem = this.element[0],
                    options = {};

                //
                if (!$.mobile.getAttribute(elem, "defaults")) {
                    for (option in this.options) {
                        value = $.mobile.getAttribute(elem, option.replace(rcapitals, replaceFunction));

                        if (value != null) {
                            options[option] = value;
                        }
                    }
                }

                return options;
            }
        });

        //TODO: Remove in 1.5 for backcompat only
        $.mobile.widget = $.Widget;

    })(jQuery);

    (function ($, undefined) {
        $.mobile.widgets = {};

        var originalWidget = $.widget,

            // Record the original, non-mobileinit-modified version of $.mobile.keepNative
            // so we can later determine whether someone has modified $.mobile.keepNative
            keepNativeFactoryDefault = $.mobile.keepNative;

        $.widget = (function (orig) {
            return function () {
                var constructor = orig.apply(this, arguments),
                    name = constructor.prototype.widgetName;

                constructor.initSelector = ((constructor.prototype.initSelector !== undefined) ?
                    constructor.prototype.initSelector : ":jqmData(role='" + name + "')");

                $.mobile.widgets[name] = constructor;

                return constructor;
            };
        })($.widget);

        // Make sure $.widget still has bridge and extend methods
        $.extend($.widget, originalWidget);

        // For backcompat remove in 1.5
        $.mobile.document.on("create", function (event) {
            $(event.target).enhanceWithin();
        });

        $.widget("mobile.page", {
            options: {
                theme: "a",
                domCache: false,

                // Deprecated in 1.4 remove in 1.5
                keepNativeDefault: $.mobile.keepNative,

                // Deprecated in 1.4 remove in 1.5
                contentTheme: null,
                enhanced: false
            },

            // DEPRECATED for > 1.4
            // TODO remove at 1.5
            _createWidget: function () {
                $.Widget.prototype._createWidget.apply(this, arguments);
                this._trigger("init");
            },

            _create: function () {
                // If false is returned by the callbacks do not create the page
                if (this._trigger("beforecreate") === false) {
                    return false;
                }

                if (!this.options.enhanced) {
                    this._enhance();
                }

                this._on(this.element, {
                    pagebeforehide: "removeContainerBackground",
                    pagebeforeshow: "_handlePageBeforeShow"
                });

                this.element.enhanceWithin();
                // Dialog widget is deprecated in 1.4 remove this in 1.5
                if ($.mobile.getAttribute(this.element[0], "role") === "dialog" && $.mobile.dialog) {
                    this.element.dialog();
                }
            },

            _enhance: function () {
                var attrPrefix = "data-" + $.mobile.ns,
                    self = this;

                if (this.options.role) {
                    this.element.attr("data-" + $.mobile.ns + "role", this.options.role);
                }

                this.element
                    .attr("tabindex", "0")
                    .addClass("ui-page ui-page-theme-" + this.options.theme);

                // Manipulation of content os Deprecated as of 1.4 remove in 1.5
                this.element.find("[" + attrPrefix + "role='content']").each(function () {
                    var $this = $(this),
                        theme = this.getAttribute(attrPrefix + "theme") || undefined;
                    self.options.contentTheme = theme || self.options.contentTheme || (self.options.dialog && self.options.theme) || (self.element.jqmData("role") === "dialog" && self.options.theme);
                    $this.addClass("ui-content");
                    if (self.options.contentTheme) {
                        $this.addClass("ui-body-" + (self.options.contentTheme));
                    }
                    // Add ARIA role
                    $this.attr("role", "main").addClass("ui-content");
                });
            },

            bindRemove: function (callback) {
                var page = this.element;

                // when dom caching is not enabled or the page is embedded bind to remove the page on hide
                if (!page.data("mobile-page").options.domCache &&
                    page.is(":jqmData(external-page='true')")) {

                    // TODO use _on - that is, sort out why it doesn't work in this case
                    page.bind("pagehide.remove", callback || function (e, data) {

                        //check if this is a same page transition and if so don't remove the page
                        if (!data.samePage) {
                            var $this = $(this),
                                prEvent = new $.Event("pageremove");

                            $this.trigger(prEvent);

                            if (!prEvent.isDefaultPrevented()) {
                                $this.removeWithDependents();
                            }
                        }
                    });
                }
            },

            _setOptions: function (o) {
                if (o.theme !== undefined) {
                    this.element.removeClass("ui-page-theme-" + this.options.theme).addClass("ui-page-theme-" + o.theme);
                }

                if (o.contentTheme !== undefined) {
                    this.element.find("[data-" + $.mobile.ns + "='content']").removeClass("ui-body-" + this.options.contentTheme)
                        .addClass("ui-body-" + o.contentTheme);
                }
            },

            _handlePageBeforeShow: function (/* e */) {
                this.setContainerBackground();
            },
            // Deprecated in 1.4 remove in 1.5
            removeContainerBackground: function () {
                this.element.closest(":mobile-pagecontainer").pagecontainer({ "theme": "none" });
            },
            // Deprecated in 1.4 remove in 1.5
            // set the page container background to the page theme
            setContainerBackground: function (theme) {
                this.element.parent().pagecontainer({ "theme": theme || this.options.theme });
            },
            // Deprecated in 1.4 remove in 1.5
            keepNativeSelector: function () {
                var options = this.options,
                    keepNative = $.trim(options.keepNative || ""),
                    globalValue = $.trim($.mobile.keepNative),
                    optionValue = $.trim(options.keepNativeDefault),

                    // Check if $.mobile.keepNative has changed from the factory default
                    newDefault = (keepNativeFactoryDefault === globalValue ?
                        "" : globalValue),

                    // If $.mobile.keepNative has not changed, use options.keepNativeDefault
                    oldDefault = (newDefault === "" ? optionValue : "");

                // Concatenate keepNative selectors from all sources where the value has
                // changed or, if nothing has changed, return the default
                return ((keepNative ? [keepNative] : [])
                    .concat(newDefault ? [newDefault] : [])
                    .concat(oldDefault ? [oldDefault] : [])
                    .join(", "));
            }
        });
    })(jQuery);

    (function ($, undefined) {

        $.mobile.degradeInputs = {
            color: false,
            date: false,
            datetime: false,
            "datetime-local": false,
            email: false,
            month: false,
            number: false,
            range: "number",
            search: "text",
            tel: false,
            time: false,
            url: false,
            week: false
        };
        // Backcompat remove in 1.5
        $.mobile.page.prototype.options.degradeInputs = $.mobile.degradeInputs;

        // Auto self-init widgets
        $.mobile.degradeInputsWithin = function (target) {

            target = $(target);

            // Degrade inputs to avoid poorly implemented native functionality
            target.find("input").not($.mobile.page.prototype.keepNativeSelector()).each(function () {
                var element = $(this),
                    type = this.getAttribute("type"),
                    optType = $.mobile.degradeInputs[type] || "text",
                    html, hasType, findstr, repstr;

                if ($.mobile.degradeInputs[type]) {
                    html = $("<div>").html(element.clone()).html();
                    // In IE browsers, the type sometimes doesn't exist in the cloned markup, so we replace the closing tag instead
                    hasType = html.indexOf(" type=") > -1;
                    findstr = hasType ? /\s+type=["']?\w+['"]?/ : /\/?>/;
                    repstr = " type=\"" + optType + "\" data-" + $.mobile.ns + "type=\"" + type + "\"" + (hasType ? "" : ">");

                    element.replaceWith(html.replace(findstr, repstr));
                }
            });

        };

    })(jQuery);


    (function ($, undefined) {

        // Deprecated in 1.4
        $.fn.fieldcontain = function (/* options */) {
            return this.addClass("ui-field-contain");
        };

    })(jQuery);

    (function ($, undefined) {

        $.fn.grid = function (options) {
            return this.each(function () {

                var $this = $(this),
                    o = $.extend({
                        grid: null
                    }, options),
                    $kids = $this.children(),
                    gridCols = { solo: 1, a: 2, b: 3, c: 4, d: 5 },
                    grid = o.grid,
                    iterator,
                    letter;

                if (!grid) {
                    if ($kids.length <= 5) {
                        for (letter in gridCols) {
                            if (gridCols[letter] === $kids.length) {
                                grid = letter;
                            }
                        }
                    } else {
                        grid = "a";
                        $this.addClass("ui-grid-duo");
                    }
                }
                iterator = gridCols[grid];

                $this.addClass("ui-grid-" + grid);

                $kids.filter(":nth-child(" + iterator + "n+1)").addClass("ui-block-a");

                if (iterator > 1) {
                    $kids.filter(":nth-child(" + iterator + "n+2)").addClass("ui-block-b");
                }
                if (iterator > 2) {
                    $kids.filter(":nth-child(" + iterator + "n+3)").addClass("ui-block-c");
                }
                if (iterator > 3) {
                    $kids.filter(":nth-child(" + iterator + "n+4)").addClass("ui-block-d");
                }
                if (iterator > 4) {
                    $kids.filter(":nth-child(" + iterator + "n+5)").addClass("ui-block-e");
                }
            });
        };
    })(jQuery);


    (function ($, undefined) {
        var path, $base, dialogHashKey = "&ui-state=dialog";

        $.mobile.path = path = {
            uiStateKey: "&ui-state",

            // This scary looking regular expression parses an absolute URL or its relative
            // variants (protocol, site, document, query, and hash), into the various
            // components (protocol, host, path, query, fragment, etc that make up the
            // URL as well as some other commonly used sub-parts. When used with RegExp.exec()
            // or String.match, it parses the URL into a results array that looks like this:
            //
            //     [0]: http://jblas:password@mycompany.com:8080/mail/inbox?msg=1234&type=unread#msg-content
            //     [1]: http://jblas:password@mycompany.com:8080/mail/inbox?msg=1234&type=unread
            //     [2]: http://jblas:password@mycompany.com:8080/mail/inbox
            //     [3]: http://jblas:password@mycompany.com:8080
            //     [4]: http:
            //     [5]: //
            //     [6]: jblas:password@mycompany.com:8080
            //     [7]: jblas:password
            //     [8]: jblas
            //     [9]: password
            //    [10]: mycompany.com:8080
            //    [11]: mycompany.com
            //    [12]: 8080
            //    [13]: /mail/inbox
            //    [14]: /mail/
            //    [15]: inbox
            //    [16]: ?msg=1234&type=unread
            //    [17]: #msg-content
            //
            urlParseRE: /^\s*(((([^:\/#\?]+:)?(?:(\/\/)((?:(([^:@\/#\?]+)(?:\:([^:@\/#\?]+))?)@)?(([^:\/#\?\]\[]+|\[[^\/\]@#?]+\])(?:\:([0-9]+))?))?)?)?((\/?(?:[^\/\?#]+\/+)*)([^\?#]*)))?(\?[^#]+)?)(#.*)?/,

            // Abstraction to address xss (Issue #4787) by removing the authority in
            // browsers that auto-decode it. All references to location.href should be
            // replaced with a call to this method so that it can be dealt with properly here
            getLocation: function (url) {
                var parsedUrl = this.parseUrl(url || location.href),
					uri = url ? parsedUrl : location,

					// Make sure to parse the url or the location object for the hash because using
					// location.hash is autodecoded in firefox, the rest of the url should be from
					// the object (location unless we're testing) to avoid the inclusion of the
					// authority
					hash = parsedUrl.hash;

                // mimic the browser with an empty string when the hash is empty
                hash = hash === "#" ? "" : hash;

                return uri.protocol +
					parsedUrl.doubleSlash +
					uri.host +

					// The pathname must start with a slash if there's a protocol, because you
					// can't have a protocol followed by a relative path. Also, it's impossible to
					// calculate absolute URLs from relative ones if the absolute one doesn't have
					// a leading "/".
					((uri.protocol !== "" && uri.pathname.substring(0, 1) !== "/") ?
						"/" : "") +
					uri.pathname +
					uri.search +
					hash;
            },

            //return the original document url
            getDocumentUrl: function (asParsedObject) {
                return asParsedObject ? $.extend({}, path.documentUrl) : path.documentUrl.href;
            },

            parseLocation: function () {
                return this.parseUrl(this.getLocation());
            },

            //Parse a URL into a structure that allows easy access to
            //all of the URL components by name.
            parseUrl: function (url) {
                // If we're passed an object, we'll assume that it is
                // a parsed url object and just return it back to the caller.
                if ($.type(url) === "object") {
                    return url;
                }

                var matches = path.urlParseRE.exec(url || "") || [];

                // Create an object that allows the caller to access the sub-matches
                // by name. Note that IE returns an empty string instead of undefined,
                // like all other browsers do, so we normalize everything so its consistent
                // no matter what browser we're running on.
                return {
                    href: matches[0] || "",
                    hrefNoHash: matches[1] || "",
                    hrefNoSearch: matches[2] || "",
                    domain: matches[3] || "",
                    protocol: matches[4] || "",
                    doubleSlash: matches[5] || "",
                    authority: matches[6] || "",
                    username: matches[8] || "",
                    password: matches[9] || "",
                    host: matches[10] || "",
                    hostname: matches[11] || "",
                    port: matches[12] || "",
                    pathname: matches[13] || "",
                    directory: matches[14] || "",
                    filename: matches[15] || "",
                    search: matches[16] || "",
                    hash: matches[17] || ""
                };
            },

            //Turn relPath into an asbolute path. absPath is
            //an optional absolute path which describes what
            //relPath is relative to.
            makePathAbsolute: function (relPath, absPath) {
                var absStack,
					relStack,
					i, d;

                if (relPath && relPath.charAt(0) === "/") {
                    return relPath;
                }

                relPath = relPath || "";
                absPath = absPath ? absPath.replace(/^\/|(\/[^\/]*|[^\/]+)$/g, "") : "";

                absStack = absPath ? absPath.split("/") : [];
                relStack = relPath.split("/");

                for (i = 0; i < relStack.length; i++) {
                    d = relStack[i];
                    switch (d) {
                        case ".":
                            break;
                        case "..":
                            if (absStack.length) {
                                absStack.pop();
                            }
                            break;
                        default:
                            absStack.push(d);
                            break;
                    }
                }
                return "/" + absStack.join("/");
            },

            //Returns true if both urls have the same domain.
            isSameDomain: function (absUrl1, absUrl2) {
                return path.parseUrl(absUrl1).domain.toLowerCase() ===
					path.parseUrl(absUrl2).domain.toLowerCase();
            },

            //Returns true for any relative variant.
            isRelativeUrl: function (url) {
                // All relative Url variants have one thing in common, no protocol.
                return path.parseUrl(url).protocol === "";
            },

            //Returns true for an absolute url.
            isAbsoluteUrl: function (url) {
                return path.parseUrl(url).protocol !== "";
            },

            //Turn the specified realtive URL into an absolute one. This function
            //can handle all relative variants (protocol, site, document, query, fragment).
            makeUrlAbsolute: function (relUrl, absUrl) {
                if (!path.isRelativeUrl(relUrl)) {
                    return relUrl;
                }

                if (absUrl === undefined) {
                    absUrl = this.documentBase;
                }

                var relObj = path.parseUrl(relUrl),
					absObj = path.parseUrl(absUrl),
					protocol = relObj.protocol || absObj.protocol,
					doubleSlash = relObj.protocol ? relObj.doubleSlash : (relObj.doubleSlash || absObj.doubleSlash),
					authority = relObj.authority || absObj.authority,
					hasPath = relObj.pathname !== "",
					pathname = path.makePathAbsolute(relObj.pathname || absObj.filename, absObj.pathname),
					search = relObj.search || (!hasPath && absObj.search) || "",
					hash = relObj.hash;

                return protocol + doubleSlash + authority + pathname + search + hash;
            },

            //Add search (aka query) params to the specified url.
            addSearchParams: function (url, params) {
                var u = path.parseUrl(url),
					p = (typeof params === "object") ? $.param(params) : params,
					s = u.search || "?";
                return u.hrefNoSearch + s + (s.charAt(s.length - 1) !== "?" ? "&" : "") + p + (u.hash || "");
            },

            convertUrlToDataUrl: function (absUrl) {
                var result = absUrl,
					u = path.parseUrl(absUrl);

                if (path.isEmbeddedPage(u)) {
                    // For embedded pages, remove the dialog hash key as in getFilePath(),
                    // and remove otherwise the Data Url won't match the id of the embedded Page.
                    result = u.hash
						.split(dialogHashKey)[0]
						.replace(/^#/, "")
						.replace(/\?.*$/, "");
                } else if (path.isSameDomain(u, this.documentBase)) {
                    result = u.hrefNoHash.replace(this.documentBase.domain, "").split(dialogHashKey)[0];
                }

                return window.decodeURIComponent(result);
            },

            //get path from current hash, or from a file path
            get: function (newPath) {
                if (newPath === undefined) {
                    newPath = path.parseLocation().hash;
                }
                return path.stripHash(newPath).replace(/[^\/]*\.[^\/*]+$/, "");
            },

            //set location hash to path
            set: function (path) {
                location.hash = path;
            },

            //test if a given url (string) is a path
            //NOTE might be exceptionally naive
            isPath: function (url) {
                return (/\//).test(url);
            },

            //return a url path with the window's location protocol/hostname/pathname removed
            clean: function (url) {
                return url.replace(this.documentBase.domain, "");
            },

            //just return the url without an initial #
            stripHash: function (url) {
                return url.replace(/^#/, "");
            },

            stripQueryParams: function (url) {
                return url.replace(/\?.*$/, "");
            },

            //remove the preceding hash, any query params, and dialog notations
            cleanHash: function (hash) {
                return path.stripHash(hash.replace(/\?.*$/, "").replace(dialogHashKey, ""));
            },

            isHashValid: function (hash) {
                return (/^#[^#]+$/).test(hash);
            },

            //check whether a url is referencing the same domain, or an external domain or different protocol
            //could be mailto, etc
            isExternal: function (url) {
                var u = path.parseUrl(url);

                return !!(u.protocol &&
					(u.domain.toLowerCase() !== this.documentUrl.domain.toLowerCase()));
            },

            hasProtocol: function (url) {
                return (/^(:?\w+:)/).test(url);
            },

            isEmbeddedPage: function (url) {
                var u = path.parseUrl(url);

                //if the path is absolute, then we need to compare the url against
                //both the this.documentUrl and the documentBase. The main reason for this
                //is that links embedded within external documents will refer to the
                //application document, whereas links embedded within the application
                //document will be resolved against the document base.
                if (u.protocol !== "") {
                    return (!this.isPath(u.hash) && u.hash && (u.hrefNoHash === this.documentUrl.hrefNoHash || (this.documentBaseDiffers && u.hrefNoHash === this.documentBase.hrefNoHash)));
                }
                return (/^#/).test(u.href);
            },

            squash: function (url, resolutionUrl) {
                var href, cleanedUrl, search, stateIndex, docUrl,
					isPath = this.isPath(url),
					uri = this.parseUrl(url),
					preservedHash = uri.hash,
					uiState = "";

                // produce a url against which we can resolve the provided path
                if (!resolutionUrl) {
                    if (isPath) {
                        resolutionUrl = path.getLocation();
                    } else {
                        docUrl = path.getDocumentUrl(true);
                        if (path.isPath(docUrl.hash)) {
                            resolutionUrl = path.squash(docUrl.href);
                        } else {
                            resolutionUrl = docUrl.href;
                        }
                    }
                }

                // If the url is anything but a simple string, remove any preceding hash
                // eg #foo/bar -> foo/bar
                //    #foo -> #foo
                cleanedUrl = isPath ? path.stripHash(url) : url;

                // If the url is a full url with a hash check if the parsed hash is a path
                // if it is, strip the #, and use it otherwise continue without change
                cleanedUrl = path.isPath(uri.hash) ? path.stripHash(uri.hash) : cleanedUrl;

                // Split the UI State keys off the href
                stateIndex = cleanedUrl.indexOf(this.uiStateKey);

                // store the ui state keys for use
                if (stateIndex > -1) {
                    uiState = cleanedUrl.slice(stateIndex);
                    cleanedUrl = cleanedUrl.slice(0, stateIndex);
                }

                // make the cleanedUrl absolute relative to the resolution url
                href = path.makeUrlAbsolute(cleanedUrl, resolutionUrl);

                // grab the search from the resolved url since parsing from
                // the passed url may not yield the correct result
                search = this.parseUrl(href).search;

                // TODO all this crap is terrible, clean it up
                if (isPath) {
                    // reject the hash if it's a path or it's just a dialog key
                    if (path.isPath(preservedHash) || preservedHash.replace("#", "").indexOf(this.uiStateKey) === 0) {
                        preservedHash = "";
                    }

                    // Append the UI State keys where it exists and it's been removed
                    // from the url
                    if (uiState && preservedHash.indexOf(this.uiStateKey) === -1) {
                        preservedHash += uiState;
                    }

                    // make sure that pound is on the front of the hash
                    if (preservedHash.indexOf("#") === -1 && preservedHash !== "") {
                        preservedHash = "#" + preservedHash;
                    }

                    // reconstruct each of the pieces with the new search string and hash
                    href = path.parseUrl(href);
                    href = href.protocol + href.doubleSlash + href.host + href.pathname + search +
						preservedHash;
                } else {
                    href += href.indexOf("#") > -1 ? uiState : "#" + uiState;
                }

                return href;
            },

            isPreservableHash: function (hash) {
                return hash.replace("#", "").indexOf(this.uiStateKey) === 0;
            },

            // Escape weird characters in the hash if it is to be used as a selector
            hashToSelector: function (hash) {
                var hasHash = (hash.substring(0, 1) === "#");
                if (hasHash) {
                    hash = hash.substring(1);
                }
                return (hasHash ? "#" : "") + hash.replace(/([!"#$%&'()*+,./:;<=>?@[\]^`{|}~])/g, "\\$1");
            },

            // return the substring of a filepath before the dialogHashKey, for making a server
            // request
            getFilePath: function (path) {
                return path && path.split(dialogHashKey)[0];
            },

            // check if the specified url refers to the first page in the main
            // application document.
            isFirstPageUrl: function (url) {
                // We only deal with absolute paths.
                var u = path.parseUrl(path.makeUrlAbsolute(url, this.documentBase)),

					// Does the url have the same path as the document?
					samePath = u.hrefNoHash === this.documentUrl.hrefNoHash ||
						(this.documentBaseDiffers &&
							u.hrefNoHash === this.documentBase.hrefNoHash),

					// Get the first page element.
					fp = $.mobile.firstPage,

					// Get the id of the first page element if it has one.
					fpId = fp && fp[0] ? fp[0].id : undefined;

                // The url refers to the first page if the path matches the document and
                // it either has no hash value, or the hash is exactly equal to the id
                // of the first page element.
                return samePath &&
					(!u.hash ||
						u.hash === "#" ||
						(fpId && u.hash.replace(/^#/, "") === fpId));
            },

            // Some embedded browsers, like the web view in Phone Gap, allow
            // cross-domain XHR requests if the document doing the request was loaded
            // via the file:// protocol. This is usually to allow the application to
            // "phone home" and fetch app specific data. We normally let the browser
            // handle external/cross-domain urls, but if the allowCrossDomainPages
            // option is true, we will allow cross-domain http/https requests to go
            // through our page loading logic.
            isPermittedCrossDomainRequest: function (docUrl, reqUrl) {
                return $.mobile.allowCrossDomainPages &&
					(docUrl.protocol === "file:" || docUrl.protocol === "content:") &&
					reqUrl.search(/^https?:/) !== -1;
            }
        };

        path.documentUrl = path.parseLocation();

        $base = $("head").find("base");

        path.documentBase = $base.length ?
			path.parseUrl(path.makeUrlAbsolute($base.attr("href"), path.documentUrl.href)) :
			path.documentUrl;

        path.documentBaseDiffers = (path.documentUrl.hrefNoHash !== path.documentBase.hrefNoHash);

        //return the original document base url
        path.getDocumentBase = function (asParsedObject) {
            return asParsedObject ? $.extend({}, path.documentBase) : path.documentBase.href;
        };

        // DEPRECATED as of 1.4.0 - remove in 1.5.0
        $.extend($.mobile, {

            //return the original document url
            getDocumentUrl: path.getDocumentUrl,

            //return the original document base url
            getDocumentBase: path.getDocumentBase
        });
    })(jQuery);



    (function ($, undefined) {
        $.mobile.History = function (stack, index) {
            this.stack = stack || [];
            this.activeIndex = index || 0;
        };

        $.extend($.mobile.History.prototype, {
            getActive: function () {
                return this.stack[this.activeIndex];
            },

            getLast: function () {
                return this.stack[this.previousIndex];
            },

            getNext: function () {
                return this.stack[this.activeIndex + 1];
            },

            getPrev: function () {
                return this.stack[this.activeIndex - 1];
            },

            // addNew is used whenever a new page is added
            add: function (url, data) {
                data = data || {};

                //if there's forward history, wipe it
                if (this.getNext()) {
                    this.clearForward();
                }

                // if the hash is included in the data make sure the shape
                // is consistent for comparison
                if (data.hash && data.hash.indexOf("#") === -1) {
                    data.hash = "#" + data.hash;
                }

                data.url = url;
                this.stack.push(data);
                this.activeIndex = this.stack.length - 1;
            },

            //wipe urls ahead of active index
            clearForward: function () {
                this.stack = this.stack.slice(0, this.activeIndex + 1);
            },

            find: function (url, stack, earlyReturn) {
                stack = stack || this.stack;

                var entry, i, length = stack.length, index;

                for (i = 0; i < length; i++) {
                    entry = stack[i];

                    if (decodeURIComponent(url) === decodeURIComponent(entry.url) ||
                        decodeURIComponent(url) === decodeURIComponent(entry.hash)) {
                        index = i;

                        if (earlyReturn) {
                            return index;
                        }
                    }
                }

                return index;
            },

            closest: function (url) {
                var closest, a = this.activeIndex;

                // First, take the slice of the history stack before the current index and search
                // for a url match. If one is found, we'll avoid avoid looking through forward history
                // NOTE the preference for backward history movement is driven by the fact that
                //      most mobile browsers only have a dedicated back button, and users rarely use
                //      the forward button in desktop browser anyhow
                closest = this.find(url, this.stack.slice(0, a));

                // If nothing was found in backward history check forward. The `true`
                // value passed as the third parameter causes the find method to break
                // on the first match in the forward history slice. The starting index
                // of the slice must then be added to the result to get the element index
                // in the original history stack :( :(
                //
                // TODO this is hyper confusing and should be cleaned up (ugh so bad)
                if (closest === undefined) {
                    closest = this.find(url, this.stack.slice(a), true);
                    closest = closest === undefined ? closest : closest + a;
                }

                return closest;
            },

            direct: function (opts) {
                var newActiveIndex = this.closest(opts.url), a = this.activeIndex;

                // save new page index, null check to prevent falsey 0 result
                // record the previous index for reference
                if (newActiveIndex !== undefined) {
                    this.activeIndex = newActiveIndex;
                    this.previousIndex = a;
                }

                // invoke callbacks where appropriate
                //
                // TODO this is also convoluted and confusing
                if (newActiveIndex < a) {
                    (opts.present || opts.back || $.noop)(this.getActive(), "back");
                } else if (newActiveIndex > a) {
                    (opts.present || opts.forward || $.noop)(this.getActive(), "forward");
                } else if (newActiveIndex === undefined && opts.missing) {
                    opts.missing(this.getActive());
                }
            }
        });
    })(jQuery);



    (function ($, undefined) {
        var path = $.mobile.path,
            initialHref = location.href;

        $.mobile.Navigator = function (history) {
            this.history = history;
            this.ignoreInitialHashChange = true;

            $.mobile.window.bind({
                "popstate.history": $.proxy(this.popstate, this),
                "hashchange.history": $.proxy(this.hashchange, this)
            });
        };

        $.extend($.mobile.Navigator.prototype, {
            squash: function (url, data) {
                var state, href, hash = path.isPath(url) ? path.stripHash(url) : url;

                href = path.squash(url);

                // make sure to provide this information when it isn't explicitly set in the
                // data object that was passed to the squash method
                state = $.extend({
                    hash: hash,
                    url: href
                }, data);

                // replace the current url with the new href and store the state
                // Note that in some cases we might be replacing an url with the
                // same url. We do this anyways because we need to make sure that
                // all of our history entries have a state object associated with
                // them. This allows us to work around the case where $.mobile.back()
                // is called to transition from an external page to an embedded page.
                // In that particular case, a hashchange event is *NOT* generated by the browser.
                // Ensuring each history entry has a state object means that onPopState()
                // will always trigger our hashchange callback even when a hashchange event
                // is not fired.
                window.history.replaceState(state, state.title || document.title, href);

                return state;
            },

            hash: function (url, href) {
                var parsed, loc, hash, resolved;

                // Grab the hash for recording. If the passed url is a path
                // we used the parsed version of the squashed url to reconstruct,
                // otherwise we assume it's a hash and store it directly
                parsed = path.parseUrl(url);
                loc = path.parseLocation();

                if (loc.pathname + loc.search === parsed.pathname + parsed.search) {
                    // If the pathname and search of the passed url is identical to the current loc
                    // then we must use the hash. Otherwise there will be no event
                    // eg, url = "/foo/bar?baz#bang", location.href = "http://example.com/foo/bar?baz"
                    hash = parsed.hash ? parsed.hash : parsed.pathname + parsed.search;
                } else if (path.isPath(url)) {
                    resolved = path.parseUrl(href);
                    // If the passed url is a path, make it domain relative and remove any trailing hash
                    hash = resolved.pathname + resolved.search + (path.isPreservableHash(resolved.hash) ? resolved.hash.replace("#", "") : "");
                } else {
                    hash = url;
                }

                return hash;
            },

            // TODO reconsider name
            go: function (url, data, noEvents) {
                var state, href, hash, popstateEvent,
                    isPopStateEvent = $.event.special.navigate.isPushStateEnabled();

                // Get the url as it would look squashed on to the current resolution url
                href = path.squash(url);

                // sort out what the hash sould be from the url
                hash = this.hash(url, href);

                // Here we prevent the next hash change or popstate event from doing any
                // history management. In the case of hashchange we don't swallow it
                // if there will be no hashchange fired (since that won't reset the value)
                // and will swallow the following hashchange
                if (noEvents && hash !== path.stripHash(path.parseLocation().hash)) {
                    this.preventNextHashChange = noEvents;
                }

                // IMPORTANT in the case where popstate is supported the event will be triggered
                //      directly, stopping further execution - ie, interupting the flow of this
                //      method call to fire bindings at this expression. Below the navigate method
                //      there is a binding to catch this event and stop its propagation.
                //
                //      We then trigger a new popstate event on the window with a null state
                //      so that the navigate events can conclude their work properly
                //
                // if the url is a path we want to preserve the query params that are available on
                // the current url.
                this.preventHashAssignPopState = true;
                window.location.hash = hash;

                // If popstate is enabled and the browser triggers `popstate` events when the hash
                // is set (this often happens immediately in browsers like Chrome), then the
                // this flag will be set to false already. If it's a browser that does not trigger
                // a `popstate` on hash assignement or `replaceState` then we need avoid the branch
                // that swallows the event created by the popstate generated by the hash assignment
                // At the time of this writing this happens with Opera 12 and some version of IE
                this.preventHashAssignPopState = false;

                state = $.extend({
                    url: href,
                    hash: hash,
                    title: document.title
                }, data);

                if (isPopStateEvent) {
                    popstateEvent = new $.Event("popstate");
                    popstateEvent.originalEvent = {
                        type: "popstate",
                        state: null
                    };

                    this.squash(url, state);

                    // Trigger a new faux popstate event to replace the one that we
                    // caught that was triggered by the hash setting above.
                    if (!noEvents) {
                        this.ignorePopState = true;
                        $.mobile.window.trigger(popstateEvent);
                    }
                }

                // record the history entry so that the information can be included
                // in hashchange event driven navigate events in a similar fashion to
                // the state that's provided by popstate
                this.history.add(state.url, state);
            },

            // This binding is intended to catch the popstate events that are fired
            // when execution of the `$.navigate` method stops at window.location.hash = url;
            // and completely prevent them from propagating. The popstate event will then be
            // retriggered after execution resumes
            //
            // TODO grab the original event here and use it for the synthetic event in the
            //      second half of the navigate execution that will follow this binding
            popstate: function (event) {
                var hash, state;

                // Partly to support our test suite which manually alters the support
                // value to test hashchange. Partly to prevent all around weirdness
                if (!$.event.special.navigate.isPushStateEnabled()) {
                    return;
                }

                // If this is the popstate triggered by the actual alteration of the hash
                // prevent it completely. History is tracked manually
                if (this.preventHashAssignPopState) {
                    this.preventHashAssignPopState = false;
                    event.stopImmediatePropagation();
                    return;
                }

                // if this is the popstate triggered after the `replaceState` call in the go
                // method, then simply ignore it. The history entry has already been captured
                if (this.ignorePopState) {
                    this.ignorePopState = false;
                    return;
                }

                // If there is no state, and the history stack length is one were
                // probably getting the page load popstate fired by browsers like chrome
                // avoid it and set the one time flag to false.
                // TODO: Do we really need all these conditions? Comparing location hrefs
                // should be sufficient.
                if (!event.originalEvent.state &&
                    this.history.stack.length === 1 &&
                    this.ignoreInitialHashChange) {
                    this.ignoreInitialHashChange = false;

                    if (location.href === initialHref) {
                        event.preventDefault();
                        return;
                    }
                }

                // account for direct manipulation of the hash. That is, we will receive a popstate
                // when the hash is changed by assignment, and it won't have a state associated. We
                // then need to squash the hash. See below for handling of hash assignment that
                // matches an existing history entry
                // TODO it might be better to only add to the history stack
                //      when the hash is adjacent to the active history entry
                hash = path.parseLocation().hash;
                if (!event.originalEvent.state && hash) {
                    // squash the hash that's been assigned on the URL with replaceState
                    // also grab the resulting state object for storage
                    state = this.squash(hash);

                    // record the new hash as an additional history entry
                    // to match the browser's treatment of hash assignment
                    this.history.add(state.url, state);

                    // pass the newly created state information
                    // along with the event
                    event.historyState = state;

                    // do not alter history, we've added a new history entry
                    // so we know where we are
                    return;
                }

                // If all else fails this is a popstate that comes from the back or forward buttons
                // make sure to set the state of our history stack properly, and record the directionality
                this.history.direct({
                    url: (event.originalEvent.state || {}).url || hash,

                    // When the url is either forward or backward in history include the entry
                    // as data on the event object for merging as data in the navigate event
                    present: function (historyEntry, direction) {
                        // make sure to create a new object to pass down as the navigate event data
                        event.historyState = $.extend({}, historyEntry);
                        event.historyState.direction = direction;
                    }
                });
            },

            // NOTE must bind before `navigate` special event hashchange binding otherwise the
            //      navigation data won't be attached to the hashchange event in time for those
            //      bindings to attach it to the `navigate` special event
            // TODO add a check here that `hashchange.navigate` is bound already otherwise it's
            //      broken (exception?)
            hashchange: function (event) {
                var history, hash;

                // If hashchange listening is explicitly disabled or pushstate is supported
                // avoid making use of the hashchange handler.
                if (!$.event.special.navigate.isHashChangeEnabled() ||
                    $.event.special.navigate.isPushStateEnabled()) {
                    return;
                }

                // On occasion explicitly want to prevent the next hash from propogating because we only
                // with to alter the url to represent the new state do so here
                if (this.preventNextHashChange) {
                    this.preventNextHashChange = false;
                    event.stopImmediatePropagation();
                    return;
                }

                history = this.history;
                hash = path.parseLocation().hash;

                // If this is a hashchange caused by the back or forward button
                // make sure to set the state of our history stack properly
                this.history.direct({
                    url: hash,

                    // When the url is either forward or backward in history include the entry
                    // as data on the event object for merging as data in the navigate event
                    present: function (historyEntry, direction) {
                        // make sure to create a new object to pass down as the navigate event data
                        event.hashchangeState = $.extend({}, historyEntry);
                        event.hashchangeState.direction = direction;
                    },

                    // When we don't find a hash in our history clearly we're aiming to go there
                    // record the entry as new for future traversal
                    //
                    // NOTE it's not entirely clear that this is the right thing to do given that we
                    //      can't know the users intention. It might be better to explicitly _not_
                    //      support location.hash assignment in preference to $.navigate calls
                    // TODO first arg to add should be the href, but it causes issues in identifying
                    //      embeded pages
                    missing: function () {
                        history.add(hash, {
                            hash: hash,
                            title: document.title
                        });
                    }
                });
            }
        });
    })(jQuery);



    (function ($, undefined) {
        // TODO consider queueing navigation activity until previous activities have completed
        //      so that end users don't have to think about it. Punting for now
        // TODO !! move the event bindings into callbacks on the navigate event
        $.mobile.navigate = function (url, data, noEvents) {
            $.mobile.navigate.navigator.go(url, data, noEvents);
        };

        // expose the history on the navigate method in anticipation of full integration with
        // existing navigation functionalty that is tightly coupled to the history information
        $.mobile.navigate.history = new $.mobile.History();

        // instantiate an instance of the navigator for use within the $.navigate method
        $.mobile.navigate.navigator = new $.mobile.Navigator($.mobile.navigate.history);

        var loc = $.mobile.path.parseLocation();
        $.mobile.navigate.history.add(loc.href, { hash: loc.hash });
    })(jQuery);



    (function ($, undefined) {

        // existing base tag?
        var baseElement = $("head").children("base"),

        // base element management, defined depending on dynamic base tag support
        // TODO move to external widget
        base = {

            // define base element, for use in routing asset urls that are referenced
            // in Ajax-requested markup
            element: (baseElement.length ? baseElement :
                $("<base>", { href: $.mobile.path.documentBase.hrefNoHash }).prependTo($("head"))),

            linkSelector: "[src], link[href], a[rel='external'], :jqmData(ajax='false'), a[target]",

            // set the generated BASE element's href to a new page's base path
            set: function (href) {

                // we should do nothing if the user wants to manage their url base
                // manually
                if (!$.mobile.dynamicBaseEnabled) {
                    return;
                }

                // we should use the base tag if we can manipulate it dynamically
                if ($.support.dynamicBaseTag) {
                    base.element.attr("href",
                        $.mobile.path.makeUrlAbsolute(href, $.mobile.path.documentBase));
                }
            },

            rewrite: function (href, page) {
                var newPath = $.mobile.path.get(href);

                page.find(base.linkSelector).each(function (i, link) {
                    var thisAttr = $(link).is("[href]") ? "href" :
                        $(link).is("[src]") ? "src" : "action",
                    theLocation = $.mobile.path.parseLocation(),
                    thisUrl = $(link).attr(thisAttr);

                    // XXX_jblas: We need to fix this so that it removes the document
                    //            base URL, and then prepends with the new page URL.
                    // if full path exists and is same, chop it - helps IE out
                    thisUrl = thisUrl.replace(theLocation.protocol + theLocation.doubleSlash +
                        theLocation.host + theLocation.pathname, "");

                    if (!/^(\w+:|#|\/)/.test(thisUrl)) {
                        $(link).attr(thisAttr, newPath + thisUrl);
                    }
                });
            },

            // set the generated BASE element's href to a new page's base path
            reset: function (/* href */) {
                base.element.attr("href", $.mobile.path.documentBase.hrefNoSearch);
            }
        };

        $.mobile.base = base;

    })(jQuery);



    (function ($, window, undefined) {

        // TODO remove direct references to $.mobile and properties, we should
        //      favor injection with params to the constructor
        $.mobile.Transition = function () {
            this.init.apply(this, arguments);
        };

        $.extend($.mobile.Transition.prototype, {
            toPreClass: " ui-page-pre-in",

            init: function (name, reverse, $to, $from) {
                $.extend(this, {
                    name: name,
                    reverse: reverse,
                    $to: $to,
                    $from: $from,
                    deferred: new $.Deferred()
                });
            },

            cleanFrom: function () {
                this.$from
                    .removeClass($.mobile.activePageClass + " out in reverse " + this.name)
                    .height("");
            },

            // NOTE overridden by child object prototypes, noop'd here as defaults
            beforeDoneIn: function () { },
            beforeDoneOut: function () { },
            beforeStartOut: function () { },

            doneIn: function () {
                this.beforeDoneIn();

                this.$to.removeClass("out in reverse " + this.name).height("");

                this.toggleViewportClass();

                // In some browsers (iOS5), 3D transitions block the ability to scroll to the desired location during transition
                // This ensures we jump to that spot after the fact, if we aren't there already.
                if ($.mobile.window.scrollTop() !== this.toScroll) {
                    this.scrollPage();
                }
                if (!this.sequential) {
                    this.$to.addClass($.mobile.activePageClass);
                }
                this.deferred.resolve(this.name, this.reverse, this.$to, this.$from, true);
            },

            doneOut: function (screenHeight, reverseClass, none, preventFocus) {
                this.beforeDoneOut();
                this.startIn(screenHeight, reverseClass, none, preventFocus);
            },

            hideIn: function (callback) {
                // Prevent flickering in phonegap container: see comments at #4024 regarding iOS
                this.$to.css("z-index", -10);
                callback.call(this);
                this.$to.css("z-index", "");
            },

            scrollPage: function () {
                // By using scrollTo instead of silentScroll, we can keep things better in order
                // Just to be precautios, disable scrollstart listening like silentScroll would
                $.event.special.scrollstart.enabled = false;
                //if we are hiding the url bar or the page was previously scrolled scroll to hide or return to position
                if ($.mobile.hideUrlBar || this.toScroll !== $.mobile.defaultHomeScroll) {
                    window.scrollTo(0, this.toScroll);
                }

                // reenable scrollstart listening like silentScroll would
                setTimeout(function () {
                    $.event.special.scrollstart.enabled = true;
                }, 150);
            },

            startIn: function (screenHeight, reverseClass, none, preventFocus) {
                this.hideIn(function () {
                    this.$to.addClass($.mobile.activePageClass + this.toPreClass);

                    // Send focus to page as it is now display: block
                    if (!preventFocus) {
                        $.mobile.focusPage(this.$to);
                    }

                    // Set to page height
                    this.$to.height(screenHeight + this.toScroll);

                    if (!none) {
                        this.scrollPage();
                    }
                });

                this.$to
                    .removeClass(this.toPreClass)
                    .addClass(this.name + " in " + reverseClass);

                if (!none) {
                    this.$to.animationComplete($.proxy(function () {
                        this.doneIn();
                    }, this));
                } else {
                    this.doneIn();
                }

            },

            startOut: function (screenHeight, reverseClass, none) {
                this.beforeStartOut(screenHeight, reverseClass, none);

                // Set the from page's height and start it transitioning out
                // Note: setting an explicit height helps eliminate tiling in the transitions
                this.$from
                    .height(screenHeight + $.mobile.window.scrollTop())
                    .addClass(this.name + " out" + reverseClass);
            },

            toggleViewportClass: function () {
                $.mobile.pageContainer.toggleClass("ui-mobile-viewport-transitioning viewport-" + this.name);
            },

            transition: function () {
                // NOTE many of these could be calculated/recorded in the constructor, it's my
                //      opinion that binding them as late as possible has value with regards to
                //      better transitions with fewer bugs. Ie, it's not guaranteed that the
                //      object will be created and transition will be run immediately after as
                //      it is today. So we wait until transition is invoked to gather the following
                var none,
                    reverseClass = this.reverse ? " reverse" : "",
                    screenHeight = $.mobile.getScreenHeight(),
                    maxTransitionOverride = $.mobile.maxTransitionWidth !== false &&
                        $.mobile.window.width() > $.mobile.maxTransitionWidth;

                this.toScroll = $.mobile.navigate.history.getActive().lastScroll || $.mobile.defaultHomeScroll;

                none = !$.support.cssTransitions || !$.support.cssAnimations ||
                    maxTransitionOverride || !this.name || this.name === "none" ||
                    Math.max($.mobile.window.scrollTop(), this.toScroll) >
                        $.mobile.getMaxScrollForTransition();

                this.toggleViewportClass();

                if (this.$from && !none) {
                    this.startOut(screenHeight, reverseClass, none);
                } else {
                    this.doneOut(screenHeight, reverseClass, none, true);
                }

                return this.deferred.promise();
            }
        });
    })(jQuery, this);


    (function ($) {

        $.mobile.SerialTransition = function () {
            this.init.apply(this, arguments);
        };

        $.extend($.mobile.SerialTransition.prototype, $.mobile.Transition.prototype, {
            sequential: true,

            beforeDoneOut: function () {
                if (this.$from) {
                    this.cleanFrom();
                }
            },

            beforeStartOut: function (screenHeight, reverseClass, none) {
                this.$from.animationComplete($.proxy(function () {
                    this.doneOut(screenHeight, reverseClass, none);
                }, this));
            }
        });

    })(jQuery);


    (function ($) {

        $.mobile.ConcurrentTransition = function () {
            this.init.apply(this, arguments);
        };

        $.extend($.mobile.ConcurrentTransition.prototype, $.mobile.Transition.prototype, {
            sequential: false,

            beforeDoneIn: function () {
                if (this.$from) {
                    this.cleanFrom();
                }
            },

            beforeStartOut: function (screenHeight, reverseClass, none) {
                this.doneOut(screenHeight, reverseClass, none);
            }
        });

    })(jQuery);


    (function ($) {

        // generate the handlers from the above
        var defaultGetMaxScrollForTransition = function () {
            return $.mobile.getScreenHeight() * 3;
        };

        //transition handler dictionary for 3rd party transitions
        $.mobile.transitionHandlers = {
            "sequential": $.mobile.SerialTransition,
            "simultaneous": $.mobile.ConcurrentTransition
        };

        // Make our transition handler the public default.
        $.mobile.defaultTransitionHandler = $.mobile.transitionHandlers.sequential;

        $.mobile.transitionFallbacks = {};

        // If transition is defined, check if css 3D transforms are supported, and if not, if a fallback is specified
        $.mobile._maybeDegradeTransition = function (transition) {
            if (transition && !$.support.cssTransform3d && $.mobile.transitionFallbacks[transition]) {
                transition = $.mobile.transitionFallbacks[transition];
            }

            return transition;
        };

        // Set the getMaxScrollForTransition to default if no implementation was set by user
        $.mobile.getMaxScrollForTransition = $.mobile.getMaxScrollForTransition || defaultGetMaxScrollForTransition;

    })(jQuery);

    (function ($, undefined) {

        $.widget("mobile.pagecontainer", {
            options: {
                theme: "a"
            },

            initSelector: false,

            _create: function () {
                this._trigger("beforecreate");
                this.setLastScrollEnabled = true;

                this._on(this.window, {
                    // disable an scroll setting when a hashchange has been fired,
                    // this only works because the recording of the scroll position
                    // is delayed for 100ms after the browser might have changed the
                    // position because of the hashchange
                    navigate: "_disableRecordScroll",

                    // bind to scrollstop for the first page, "pagechange" won't be
                    // fired in that case
                    scrollstop: "_delayedRecordScroll"
                });

                // TODO consider moving the navigation handler OUT of widget into
                //      some other object as glue between the navigate event and the
                //      content widget load and change methods
                this._on(this.window, { navigate: "_filterNavigateEvents" });

                // TODO move from page* events to content* events
                this._on({ pagechange: "_afterContentChange" });

                // handle initial hashchange from chrome :(
                this.window.one("navigate", $.proxy(function () {
                    this.setLastScrollEnabled = true;
                }, this));
            },

            _setOptions: function (options) {
                if (options.theme !== undefined && options.theme !== "none") {
                    this.element.removeClass("ui-overlay-" + this.options.theme)
                        .addClass("ui-overlay-" + options.theme);
                } else if (options.theme !== undefined) {
                    this.element.removeClass("ui-overlay-" + this.options.theme);
                }

                this._super(options);
            },

            _disableRecordScroll: function () {
                this.setLastScrollEnabled = false;
            },

            _enableRecordScroll: function () {
                this.setLastScrollEnabled = true;
            },

            // TODO consider the name here, since it's purpose specific
            _afterContentChange: function () {
                // once the page has changed, re-enable the scroll recording
                this.setLastScrollEnabled = true;

                // remove any binding that previously existed on the get scroll
                // which may or may not be different than the scroll element
                // determined for this page previously
                this._off(this.window, "scrollstop");

                // determine and bind to the current scoll element which may be the
                // window or in the case of touch overflow the element touch overflow
                this._on(this.window, { scrollstop: "_delayedRecordScroll" });
            },

            _recordScroll: function () {
                // this barrier prevents setting the scroll value based on
                // the browser scrolling the window based on a hashchange
                if (!this.setLastScrollEnabled) {
                    return;
                }

                var active = this._getActiveHistory(),
                    currentScroll, minScroll, defaultScroll;

                if (active) {
                    currentScroll = this._getScroll();
                    minScroll = this._getMinScroll();
                    defaultScroll = this._getDefaultScroll();

                    // Set active page's lastScroll prop. If the location we're
                    // scrolling to is less than minScrollBack, let it go.
                    active.lastScroll = currentScroll < minScroll ? defaultScroll : currentScroll;
                }
            },

            _delayedRecordScroll: function () {
                setTimeout($.proxy(this, "_recordScroll"), 100);
            },

            _getScroll: function () {
                return this.window.scrollTop();
            },

            _getMinScroll: function () {
                return $.mobile.minScrollBack;
            },

            _getDefaultScroll: function () {
                return $.mobile.defaultHomeScroll;
            },

            _filterNavigateEvents: function (e, data) {
                var url;

                if (e.originalEvent && e.originalEvent.isDefaultPrevented()) {
                    return;
                }

                url = e.originalEvent.type.indexOf("hashchange") > -1 ? data.state.hash : data.state.url;

                if (!url) {
                    url = this._getHash();
                }

                if (!url || url === "#" || url.indexOf("#" + $.mobile.path.uiStateKey) === 0) {
                    url = location.href;
                }

                this._handleNavigate(url, data.state);
            },

            _getHash: function () {
                return $.mobile.path.parseLocation().hash;
            },

            // TODO active page should be managed by the container (ie, it should be a property)
            getActivePage: function () {
                return this.activePage;
            },

            // TODO the first page should be a property set during _create using the logic
            //      that currently resides in init
            _getInitialContent: function () {
                return $.mobile.firstPage;
            },

            // TODO each content container should have a history object
            _getHistory: function () {
                return $.mobile.navigate.history;
            },

            _getActiveHistory: function () {
                return this._getHistory().getActive();
            },

            // TODO the document base should be determined at creation
            _getDocumentBase: function () {
                return $.mobile.path.documentBase;
            },

            back: function () {
                this.go(-1);
            },

            forward: function () {
                this.go(1);
            },

            go: function (steps) {

                //if hashlistening is enabled use native history method
                if ($.mobile.hashListeningEnabled) {
                    window.history.go(steps);
                } else {

                    //we are not listening to the hash so handle history internally
                    var activeIndex = $.mobile.navigate.history.activeIndex,
                        index = activeIndex + parseInt(steps, 10),
                        url = $.mobile.navigate.history.stack[index].url,
                        direction = (steps >= 1) ? "forward" : "back";

                    //update the history object
                    $.mobile.navigate.history.activeIndex = index;
                    $.mobile.navigate.history.previousIndex = activeIndex;

                    //change to the new page
                    this.change(url, { direction: direction, changeHash: false, fromHashChange: true });
                }
            },

            // TODO rename _handleDestination
            _handleDestination: function (to) {
                var history;

                // clean the hash for comparison if it's a url
                if ($.type(to) === "string") {
                    to = $.mobile.path.stripHash(to);
                }

                if (to) {
                    history = this._getHistory();

                    // At this point, 'to' can be one of 3 things, a cached page
                    // element from a history stack entry, an id, or site-relative /
                    // absolute URL. If 'to' is an id, we need to resolve it against
                    // the documentBase, not the location.href, since the hashchange
                    // could've been the result of a forward/backward navigation
                    // that crosses from an external page/dialog to an internal
                    // page/dialog.
                    //
                    // TODO move check to history object or path object?
                    to = !$.mobile.path.isPath(to) ? ($.mobile.path.makeUrlAbsolute("#" + to, this._getDocumentBase())) : to;
                }
                return to || this._getInitialContent();
            },

            _transitionFromHistory: function (direction, defaultTransition) {
                var history = this._getHistory(),
                    entry = (direction === "back" ? history.getLast() : history.getActive());

                return (entry && entry.transition) || defaultTransition;
            },

            _handleDialog: function (changePageOptions, data) {
                var to, active, activeContent = this.getActivePage();

                // If current active page is not a dialog skip the dialog and continue
                // in the same direction
                // Note: The dialog widget is deprecated as of 1.4.0 and will be removed in 1.5.0.
                // Thus, as of 1.5.0 activeContent.data( "mobile-dialog" ) will always evaluate to
                // falsy, so the second condition in the if-statement below can be removed altogether.
                if (activeContent && !activeContent.data("mobile-dialog")) {
                    // determine if we're heading forward or backward and continue
                    // accordingly past the current dialog
                    if (data.direction === "back") {
                        this.back();
                    } else {
                        this.forward();
                    }

                    // prevent changePage call
                    return false;
                } else {
                    // if the current active page is a dialog and we're navigating
                    // to a dialog use the dialog objected saved in the stack
                    to = data.pageUrl;
                    active = this._getActiveHistory();

                    // make sure to set the role, transition and reversal
                    // as most of this is lost by the domCache cleaning
                    $.extend(changePageOptions, {
                        role: active.role,
                        transition: this._transitionFromHistory(
                            data.direction,
                            changePageOptions.transition),
                        reverse: data.direction === "back"
                    });
                }

                return to;
            },

            _handleNavigate: function (url, data) {
                //find first page via hash
                // TODO stripping the hash twice with handleUrl
                var to = $.mobile.path.stripHash(url), history = this._getHistory(),

                    // transition is false if it's the first page, undefined
                    // otherwise (and may be overridden by default)
                    transition = history.stack.length === 0 ? "none" :
                        this._transitionFromHistory(data.direction),

                    // default options for the changPage calls made after examining
                    // the current state of the page and the hash, NOTE that the
                    // transition is derived from the previous history entry
                    changePageOptions = {
                        changeHash: false,
                        fromHashChange: true,
                        reverse: data.direction === "back"
                    };

                $.extend(changePageOptions, data, {
                    transition: transition
                });

                // TODO move to _handleDestination ?
                // If this isn't the first page, if the current url is a dialog hash
                // key, and the initial destination isn't equal to the current target
                // page, use the special dialog handling
                if (history.activeIndex > 0 &&
                    to.indexOf($.mobile.dialogHashKey) > -1) {

                    to = this._handleDialog(changePageOptions, data);

                    if (to === false) {
                        return;
                    }
                }

                this._changeContent(this._handleDestination(to), changePageOptions);
            },

            _changeContent: function (to, opts) {
                $.mobile.changePage(to, opts);
            },

            _getBase: function () {
                return $.mobile.base;
            },

            _getNs: function () {
                return $.mobile.ns;
            },

            _enhance: function (content, role) {
                // TODO consider supporting a custom callback, and passing in
                // the settings which includes the role
                return content.page({ role: role });
            },

            _include: function (page, settings) {
                // append to page and enhance
                page.appendTo(this.element);

                // use the page widget to enhance
                this._enhance(page, settings.role);

                // remove page on hide
                page.page("bindRemove");
            },

            _find: function (absUrl) {
                // TODO consider supporting a custom callback
                var fileUrl = this._createFileUrl(absUrl),
                    dataUrl = this._createDataUrl(absUrl),
                    page, initialContent = this._getInitialContent();

                // Check to see if the page already exists in the DOM.
                // NOTE do _not_ use the :jqmData pseudo selector because parenthesis
                //      are a valid url char and it breaks on the first occurence
                page = this.element
                    .children("[data-" + this._getNs() +
                        "url='" + $.mobile.path.hashToSelector(dataUrl) + "']");

                // If we failed to find the page, check to see if the url is a
                // reference to an embedded page. If so, it may have been dynamically
                // injected by a developer, in which case it would be lacking a
                // data-url attribute and in need of enhancement.
                if (page.length === 0 && dataUrl && !$.mobile.path.isPath(dataUrl)) {
                    page = this.element.children($.mobile.path.hashToSelector("#" + dataUrl))
                        .attr("data-" + this._getNs() + "url", dataUrl)
                        .jqmData("url", dataUrl);
                }

                // If we failed to find a page in the DOM, check the URL to see if it
                // refers to the first page in the application. Also check to make sure
                // our cached-first-page is actually in the DOM. Some user deployed
                // apps are pruning the first page from the DOM for various reasons.
                // We check for this case here because we don't want a first-page with
                // an id falling through to the non-existent embedded page error case.
                if (page.length === 0 &&
                    $.mobile.path.isFirstPageUrl(fileUrl) &&
                    initialContent &&
                    initialContent.parent().length) {
                    page = $(initialContent);
                }

                return page;
            },

            _getLoader: function () {
                return $.mobile.loading();
            },

            _showLoading: function (delay, theme, msg, textonly) {
                // This configurable timeout allows cached pages a brief
                // delay to load without showing a message
                if (this._loadMsg) {
                    return;
                }

                this._loadMsg = setTimeout($.proxy(function () {
                    this._getLoader().loader("show", theme, msg, textonly);
                    this._loadMsg = 0;
                }, this), delay);
            },

            _hideLoading: function () {
                // Stop message show timer
                clearTimeout(this._loadMsg);
                this._loadMsg = 0;

                // Hide loading message
                this._getLoader().loader("hide");
            },

            _showError: function () {
                // make sure to remove the current loading message
                this._hideLoading();

                // show the error message
                this._showLoading(0, $.mobile.pageLoadErrorMessageTheme, $.mobile.pageLoadErrorMessage, true);

                // hide the error message after a delay
                // TODO configuration
                setTimeout($.proxy(this, "_hideLoading"), 1500);
            },

            _parse: function (html, fileUrl) {
                // TODO consider allowing customization of this method. It's very JQM specific
                var page, all = $("<div></div>");

                //workaround to allow scripts to execute when included in page divs
                all.get(0).innerHTML = html;

                page = all.find(":jqmData(role='page'), :jqmData(role='dialog')").first();

                //if page elem couldn't be found, create one and insert the body element's contents
                if (!page.length) {
                    page = $("<div data-" + this._getNs() + "role='page'>" +
                        (html.split(/<\/?body[^>]*>/gmi)[1] || "") +
                        "</div>");
                }

                // TODO tagging a page with external to make sure that embedded pages aren't
                // removed by the various page handling code is bad. Having page handling code
                // in many places is bad. Solutions post 1.0
                page.attr("data-" + this._getNs() + "url", this._createDataUrl(fileUrl))
                    .attr("data-" + this._getNs() + "external-page", true);

                return page;
            },

            _setLoadedTitle: function (page, html) {
                //page title regexp
                var newPageTitle = html.match(/<title[^>]*>([^<]*)/) && RegExp.$1;

                if (newPageTitle && !page.jqmData("title")) {
                    newPageTitle = $("<div>" + newPageTitle + "</div>").text();
                    page.jqmData("title", newPageTitle);
                }
            },

            _isRewritableBaseTag: function () {
                return $.mobile.dynamicBaseEnabled && !$.support.dynamicBaseTag;
            },

            _createDataUrl: function (absoluteUrl) {
                return $.mobile.path.convertUrlToDataUrl(absoluteUrl);
            },

            _createFileUrl: function (absoluteUrl) {
                return $.mobile.path.getFilePath(absoluteUrl);
            },

            _triggerWithDeprecated: function (name, data, page) {
                var deprecatedEvent = $.Event("page" + name),
                    newEvent = $.Event(this.widgetName + name);

                // DEPRECATED
                // trigger the old deprecated event on the page if it's provided
                (page || this.element).trigger(deprecatedEvent, data);

                // use the widget trigger method for the new content* event
                this._trigger(name, newEvent, data);

                return {
                    deprecatedEvent: deprecatedEvent,
                    event: newEvent
                };
            },

            // TODO it would be nice to split this up more but everything appears to be "one off"
            //      or require ordering such that other bits are sprinkled in between parts that
            //      could be abstracted out as a group
            _loadSuccess: function (absUrl, triggerData, settings, deferred) {
                var fileUrl = this._createFileUrl(absUrl);

                return $.proxy(function (html, textStatus, xhr) {
                    //pre-parse html to check for a data-url,
                    //use it as the new fileUrl, base path, etc
                    var content,

                        // TODO handle dialogs again
                        pageElemRegex = new RegExp("(<[^>]+\\bdata-" + this._getNs() + "role=[\"']?page[\"']?[^>]*>)"),

                        dataUrlRegex = new RegExp("\\bdata-" + this._getNs() + "url=[\"']?([^\"'>]*)[\"']?");

                    // data-url must be provided for the base tag so resource requests
                    // can be directed to the correct url. loading into a temprorary
                    // element makes these requests immediately
                    if (pageElemRegex.test(html) &&
                        RegExp.$1 &&
                        dataUrlRegex.test(RegExp.$1) &&
                        RegExp.$1) {
                        fileUrl = $.mobile.path.getFilePath($("<div>" + RegExp.$1 + "</div>").text());

                        // We specify that, if a data-url attribute is given on the page div, its value
                        // must be given non-URL-encoded. However, in this part of the code, fileUrl is
                        // assumed to be URL-encoded, so we URL-encode the retrieved value here
                        fileUrl = this.window[0].encodeURIComponent(fileUrl);
                    }

                    //dont update the base tag if we are prefetching
                    if (settings.prefetch === undefined) {
                        this._getBase().set(fileUrl);
                    }

                    content = this._parse(html, fileUrl);

                    this._setLoadedTitle(content, html);

                    // Add the content reference and xhr to our triggerData.
                    triggerData.xhr = xhr;
                    triggerData.textStatus = textStatus;

                    // DEPRECATED
                    triggerData.page = content;

                    triggerData.content = content;

                    triggerData.toPage = content;

                    // If the default behavior is prevented, stop here!
                    // Note that it is the responsibility of the listener/handler
                    // that called preventDefault(), to resolve/reject the
                    // deferred object within the triggerData.
                    if (this._triggerWithDeprecated("load", triggerData).event.isDefaultPrevented()) {
                        return;
                    }

                    // rewrite src and href attrs to use a base url if the base tag won't work
                    if (this._isRewritableBaseTag() && content) {
                        this._getBase().rewrite(fileUrl, content);
                    }

                    this._include(content, settings);

                    // Remove loading message.
                    if (settings.showLoadMsg) {
                        this._hideLoading();
                    }

                    deferred.resolve(absUrl, settings, content);
                }, this);
            },

            _loadDefaults: {
                type: "get",
                data: undefined,

                // DEPRECATED
                reloadPage: false,

                reload: false,

                // By default we rely on the role defined by the @data-role attribute.
                role: undefined,

                showLoadMsg: false,

                // This delay allows loads that pull from browser cache to
                // occur without showing the loading message.
                loadMsgDelay: 50
            },

            load: function (url, options) {
                // This function uses deferred notifications to let callers
                // know when the content is done loading, or if an error has occurred.
                var deferred = (options && options.deferred) || $.Deferred(),

                    // Examining the option "reloadPage" passed by the user is deprecated as of 1.4.0
                    // and will be removed in 1.5.0.
                    // Copy option "reloadPage" to "reload", but only if option "reload" is not present
                    reloadOptionExtension =
                        ((options && options.reload === undefined &&
                            options.reloadPage !== undefined) ?
							{ reload: options.reloadPage } : {}),

                    // The default load options with overrides specified by the caller.
                    settings = $.extend({}, this._loadDefaults, options, reloadOptionExtension),

                    // The DOM element for the content after it has been loaded.
                    content = null,

                    // The absolute version of the URL passed into the function. This
                    // version of the URL may contain dialog/subcontent params in it.
                    absUrl = $.mobile.path.makeUrlAbsolute(url, this._findBaseWithDefault()),
                    fileUrl, dataUrl, pblEvent, triggerData;

                // If the caller provided data, and we're using "get" request,
                // append the data to the URL.
                if (settings.data && settings.type === "get") {
                    absUrl = $.mobile.path.addSearchParams(absUrl, settings.data);
                    settings.data = undefined;
                }

                // If the caller is using a "post" request, reload must be true
                if (settings.data && settings.type === "post") {
                    settings.reload = true;
                }

                // The absolute version of the URL minus any dialog/subcontent params.
                // In otherwords the real URL of the content to be loaded.
                fileUrl = this._createFileUrl(absUrl);

                // The version of the Url actually stored in the data-url attribute of
                // the content. For embedded content, it is just the id of the page. For
                // content within the same domain as the document base, it is the site
                // relative path. For cross-domain content (Phone Gap only) the entire
                // absolute Url is used to load the content.
                dataUrl = this._createDataUrl(absUrl);

                content = this._find(absUrl);

                // If it isn't a reference to the first content and refers to missing
                // embedded content reject the deferred and return
                if (content.length === 0 &&
                    $.mobile.path.isEmbeddedPage(fileUrl) &&
                    !$.mobile.path.isFirstPageUrl(fileUrl)) {
                    deferred.reject(absUrl, settings);
                    return deferred.promise();
                }

                // Reset base to the default document base
                // TODO figure out why we doe this
                this._getBase().reset();

                // If the content we are interested in is already in the DOM,
                // and the caller did not indicate that we should force a
                // reload of the file, we are done. Resolve the deferrred so that
                // users can bind to .done on the promise
                if (content.length && !settings.reload) {
                    this._enhance(content, settings.role);
                    deferred.resolve(absUrl, settings, content);

                    //if we are reloading the content make sure we update
                    // the base if its not a prefetch
                    if (!settings.prefetch) {
                        this._getBase().set(url);
                    }

                    return deferred.promise();
                }

                triggerData = {
                    url: url,
                    absUrl: absUrl,
                    toPage: url,
                    prevPage: options ? options.fromPage : undefined,
                    dataUrl: dataUrl,
                    deferred: deferred,
                    options: settings
                };

                // Let listeners know we're about to load content.
                pblEvent = this._triggerWithDeprecated("beforeload", triggerData);

                // If the default behavior is prevented, stop here!
                if (pblEvent.deprecatedEvent.isDefaultPrevented() ||
                    pblEvent.event.isDefaultPrevented()) {
                    return deferred.promise();
                }

                if (settings.showLoadMsg) {
                    this._showLoading(settings.loadMsgDelay);
                }

                // Reset base to the default document base.
                // only reset if we are not prefetching
                if (settings.prefetch === undefined) {
                    this._getBase().reset();
                }

                if (!($.mobile.allowCrossDomainPages ||
                    $.mobile.path.isSameDomain($.mobile.path.documentUrl, absUrl))) {
                    deferred.reject(absUrl, settings);
                    return deferred.promise();
                }

                // Load the new content.
                $.ajax({
                    url: fileUrl,
                    type: settings.type,
                    data: settings.data,
                    contentType: settings.contentType,
                    dataType: "html",
                    success: this._loadSuccess(absUrl, triggerData, settings, deferred),
                    error: this._loadError(absUrl, triggerData, settings, deferred)
                });

                return deferred.promise();
            },

            _loadError: function (absUrl, triggerData, settings, deferred) {
                return $.proxy(function (xhr, textStatus, errorThrown) {
                    //set base back to current path
                    this._getBase().set($.mobile.path.get());

                    // Add error info to our triggerData.
                    triggerData.xhr = xhr;
                    triggerData.textStatus = textStatus;
                    triggerData.errorThrown = errorThrown;

                    // Let listeners know the page load failed.
                    var plfEvent = this._triggerWithDeprecated("loadfailed", triggerData);

                    // If the default behavior is prevented, stop here!
                    // Note that it is the responsibility of the listener/handler
                    // that called preventDefault(), to resolve/reject the
                    // deferred object within the triggerData.
                    if (plfEvent.deprecatedEvent.isDefaultPrevented() ||
                        plfEvent.event.isDefaultPrevented()) {
                        return;
                    }

                    // Remove loading message.
                    if (settings.showLoadMsg) {
                        this._showError();
                    }

                    deferred.reject(absUrl, settings);
                }, this);
            },

            _getTransitionHandler: function (transition) {
                transition = $.mobile._maybeDegradeTransition(transition);

                //find the transition handler for the specified transition. If there
                //isn't one in our transitionHandlers dictionary, use the default one.
                //call the handler immediately to kick-off the transition.
                return $.mobile.transitionHandlers[transition] || $.mobile.defaultTransitionHandler;
            },

            // TODO move into transition handlers?
            _triggerCssTransitionEvents: function (to, from, prefix) {
                var samePage = false;

                prefix = prefix || "";

                // TODO decide if these events should in fact be triggered on the container
                if (from) {

                    //Check if this is a same page transition and tell the handler in page
                    if (to[0] === from[0]) {
                        samePage = true;
                    }

                    //trigger before show/hide events
                    // TODO deprecate nextPage in favor of next
                    this._triggerWithDeprecated(prefix + "hide", {

                        // Deprecated in 1.4 remove in 1.5
                        nextPage: to,
                        toPage: to,
                        prevPage: from,
                        samePage: samePage
                    }, from);
                }

                // TODO deprecate prevPage in favor of previous
                this._triggerWithDeprecated(prefix + "show", {
                    prevPage: from || $(""),
                    toPage: to
                }, to);
            },

            // TODO make private once change has been defined in the widget
            _cssTransition: function (to, from, options) {
                var transition = options.transition,
                    reverse = options.reverse,
                    deferred = options.deferred,
                    TransitionHandler,
                    promise;

                this._triggerCssTransitionEvents(to, from, "before");

                // TODO put this in a binding to events *outside* the widget
                this._hideLoading();

                TransitionHandler = this._getTransitionHandler(transition);

                promise = (new TransitionHandler(transition, reverse, to, from)).transition();

                promise.done($.proxy(function () {
                    this._triggerCssTransitionEvents(to, from);
                }, this));

                // TODO temporary accomodation of argument deferred
                promise.done(function () {
                    deferred.resolve.apply(deferred, arguments);
                });
            },

            _releaseTransitionLock: function () {
                //release transition lock so navigation is free again
                isPageTransitioning = false;
                if (pageTransitionQueue.length > 0) {
                    $.mobile.changePage.apply(null, pageTransitionQueue.pop());
                }
            },

            _removeActiveLinkClass: function (force) {
                //clear out the active button state
                $.mobile.removeActiveLinkClass(force);
            },

            _loadUrl: function (to, triggerData, settings) {
                // preserve the original target as the dataUrl value will be
                // simplified eg, removing ui-state, and removing query params
                // from the hash this is so that users who want to use query
                // params have access to them in the event bindings for the page
                // life cycle See issue #5085
                settings.target = to;
                settings.deferred = $.Deferred();

                this.load(to, settings);

                settings.deferred.done($.proxy(function (url, options, content) {
                    isPageTransitioning = false;

                    // store the original absolute url so that it can be provided
                    // to events in the triggerData of the subsequent changePage call
                    options.absUrl = triggerData.absUrl;

                    this.transition(content, triggerData, options);
                }, this));

                settings.deferred.fail($.proxy(function (/* url, options */) {
                    this._removeActiveLinkClass(true);
                    this._releaseTransitionLock();
                    this._triggerWithDeprecated("changefailed", triggerData);
                }, this));
            },

            _triggerPageBeforeChange: function (to, triggerData, settings) {
                var returnEvents;

                triggerData.prevPage = this.activePage;
                $.extend(triggerData, {
                    toPage: to,
                    options: settings
                });

                // NOTE: preserve the original target as the dataUrl value will be
                // simplified eg, removing ui-state, and removing query params from
                // the hash this is so that users who want to use query params have
                // access to them in the event bindings for the page life cycle
                // See issue #5085
                if ($.type(to) === "string") {
                    // if the toPage is a string simply convert it
                    triggerData.absUrl = $.mobile.path.makeUrlAbsolute(to, this._findBaseWithDefault());
                } else {
                    // if the toPage is a jQuery object grab the absolute url stored
                    // in the loadPage callback where it exists
                    triggerData.absUrl = settings.absUrl;
                }

                // Let listeners know we're about to change the current page.
                returnEvents = this._triggerWithDeprecated("beforechange", triggerData);

                // If the default behavior is prevented, stop here!
                if (returnEvents.event.isDefaultPrevented() ||
                    returnEvents.deprecatedEvent.isDefaultPrevented()) {
                    return false;
                }

                return true;
            },

            change: function (to, options) {
                // If we are in the midst of a transition, queue the current request.
                // We'll call changePage() once we're done with the current transition
                // to service the request.
                if (isPageTransitioning) {
                    pageTransitionQueue.unshift(arguments);
                    return;
                }

                var settings = $.extend({}, $.mobile.changePage.defaults, options),
                    triggerData = {};

                // Make sure we have a fromPage.
                settings.fromPage = settings.fromPage || this.activePage;

                // if the page beforechange default is prevented return early
                if (!this._triggerPageBeforeChange(to, triggerData, settings)) {
                    return;
                }

                // We allow "pagebeforechange" observers to modify the to in
                // the trigger data to allow for redirects. Make sure our to is
                // updated. We also need to re-evaluate whether it is a string,
                // because an object can also be replaced by a string
                to = triggerData.toPage;

                // If the caller passed us a url, call loadPage()
                // to make sure it is loaded into the DOM. We'll listen
                // to the promise object it returns so we know when
                // it is done loading or if an error ocurred.
                if ($.type(to) === "string") {
                    // Set the isPageTransitioning flag to prevent any requests from
                    // entering this method while we are in the midst of loading a page
                    // or transitioning.
                    isPageTransitioning = true;

                    this._loadUrl(to, triggerData, settings);
                } else {
                    this.transition(to, triggerData, settings);
                }
            },

            transition: function (toPage, triggerData, settings) {
                var fromPage, url, pageUrl, fileUrl,
                    active, activeIsInitialPage,
                    historyDir, pageTitle, isDialog,
                    alreadyThere, newPageTitle,
                    params, cssTransitionDeferred,
                    beforeTransition;

                // If we are in the midst of a transition, queue the current request.
                // We'll call changePage() once we're done with the current transition
                // to service the request.
                if (isPageTransitioning) {
                    // make sure to only queue the to and settings values so the arguments
                    // work with a call to the change method
                    pageTransitionQueue.unshift([toPage, settings]);
                    return;
                }

                // DEPRECATED - this call only, in favor of the before transition
                // if the page beforechange default is prevented return early
                if (!this._triggerPageBeforeChange(toPage, triggerData, settings)) {
                    return;
                }

                triggerData.prevPage = settings.fromPage;
                // if the (content|page)beforetransition default is prevented return early
                // Note, we have to check for both the deprecated and new events
                beforeTransition = this._triggerWithDeprecated("beforetransition", triggerData);
                if (beforeTransition.deprecatedEvent.isDefaultPrevented() ||
                    beforeTransition.event.isDefaultPrevented()) {
                    return;
                }

                // Set the isPageTransitioning flag to prevent any requests from
                // entering this method while we are in the midst of loading a page
                // or transitioning.
                isPageTransitioning = true;

                // If we are going to the first-page of the application, we need to make
                // sure settings.dataUrl is set to the application document url. This allows
                // us to avoid generating a document url with an id hash in the case where the
                // first-page of the document has an id attribute specified.
                if (toPage[0] === $.mobile.firstPage[0] && !settings.dataUrl) {
                    settings.dataUrl = $.mobile.path.documentUrl.hrefNoHash;
                }

                // The caller passed us a real page DOM element. Update our
                // internal state and then trigger a transition to the page.
                fromPage = settings.fromPage;
                url = (settings.dataUrl && $.mobile.path.convertUrlToDataUrl(settings.dataUrl)) ||
                    toPage.jqmData("url");

                // The pageUrl var is usually the same as url, except when url is obscured
                // as a dialog url. pageUrl always contains the file path
                pageUrl = url;
                fileUrl = $.mobile.path.getFilePath(url);
                active = $.mobile.navigate.history.getActive();
                activeIsInitialPage = $.mobile.navigate.history.activeIndex === 0;
                historyDir = 0;
                pageTitle = document.title;
                isDialog = (settings.role === "dialog" ||
                    toPage.jqmData("role") === "dialog") &&
                    toPage.jqmData("dialog") !== true;

                // By default, we prevent changePage requests when the fromPage and toPage
                // are the same element, but folks that generate content
                // manually/dynamically and reuse pages want to be able to transition to
                // the same page. To allow this, they will need to change the default
                // value of allowSamePageTransition to true, *OR*, pass it in as an
                // option when they manually call changePage(). It should be noted that
                // our default transition animations assume that the formPage and toPage
                // are different elements, so they may behave unexpectedly. It is up to
                // the developer that turns on the allowSamePageTransitiona option to
                // either turn off transition animations, or make sure that an appropriate
                // animation transition is used.
                if (fromPage && fromPage[0] === toPage[0] &&
                    !settings.allowSamePageTransition) {

                    isPageTransitioning = false;
                    this._triggerWithDeprecated("transition", triggerData);
                    this._triggerWithDeprecated("change", triggerData);

                    // Even if there is no page change to be done, we should keep the
                    // urlHistory in sync with the hash changes
                    if (settings.fromHashChange) {
                        $.mobile.navigate.history.direct({ url: url });
                    }

                    return;
                }

                // We need to make sure the page we are given has already been enhanced.
                toPage.page({ role: settings.role });

                // If the changePage request was sent from a hashChange event, check to
                // see if the page is already within the urlHistory stack. If so, we'll
                // assume the user hit the forward/back button and will try to match the
                // transition accordingly.
                if (settings.fromHashChange) {
                    historyDir = settings.direction === "back" ? -1 : 1;
                }

                // Kill the keyboard.
                // XXX_jblas: We need to stop crawling the entire document to kill focus.
                //            Instead, we should be tracking focus with a delegate()
                //            handler so we already have the element in hand at this
                //            point.
                // Wrap this in a try/catch block since IE9 throw "Unspecified error" if
                // document.activeElement is undefined when we are in an IFrame.
                try {
                    if (document.activeElement &&
                        document.activeElement.nodeName.toLowerCase() !== "body") {

                        $(document.activeElement).blur();
                    } else {
                        $("input:focus, textarea:focus, select:focus").blur();
                    }
                } catch (e) { }

                // Record whether we are at a place in history where a dialog used to be -
                // if so, do not add a new history entry and do not change the hash either
                alreadyThere = false;

                // If we're displaying the page as a dialog, we don't want the url
                // for the dialog content to be used in the hash. Instead, we want
                // to append the dialogHashKey to the url of the current page.
                if (isDialog && active) {
                    // on the initial page load active.url is undefined and in that case
                    // should be an empty string. Moving the undefined -> empty string back
                    // into urlHistory.addNew seemed imprudent given undefined better
                    // represents the url state

                    // If we are at a place in history that once belonged to a dialog, reuse
                    // this state without adding to urlHistory and without modifying the
                    // hash. However, if a dialog is already displayed at this point, and
                    // we're about to display another dialog, then we must add another hash
                    // and history entry on top so that one may navigate back to the
                    // original dialog
                    if (active.url &&
                        active.url.indexOf($.mobile.dialogHashKey) > -1 &&
                        this.activePage &&
                        !this.activePage.hasClass("ui-dialog") &&
                        $.mobile.navigate.history.activeIndex > 0) {

                        settings.changeHash = false;
                        alreadyThere = true;
                    }

                    // Normally, we tack on a dialog hash key, but if this is the location
                    // of a stale dialog, we reuse the URL from the entry
                    url = (active.url || "");

                    // account for absolute urls instead of just relative urls use as hashes
                    if (!alreadyThere && url.indexOf("#") > -1) {
                        url += $.mobile.dialogHashKey;
                    } else {
                        url += "#" + $.mobile.dialogHashKey;
                    }
                }

                // if title element wasn't found, try the page div data attr too
                // If this is a deep-link or a reload ( active === undefined ) then just
                // use pageTitle
                newPageTitle = (!active) ? pageTitle : toPage.jqmData("title") ||
                    toPage.children(":jqmData(role='header')").find(".ui-title").text();
                if (!!newPageTitle && pageTitle === document.title) {
                    pageTitle = newPageTitle;
                }
                if (!toPage.jqmData("title")) {
                    toPage.jqmData("title", pageTitle);
                }

                // Make sure we have a transition defined.
                settings.transition = settings.transition ||
                    ((historyDir && !activeIsInitialPage) ? active.transition : undefined) ||
                    (isDialog ? $.mobile.defaultDialogTransition : $.mobile.defaultPageTransition);

                //add page to history stack if it's not back or forward
                if (!historyDir && alreadyThere) {
                    $.mobile.navigate.history.getActive().pageUrl = pageUrl;
                }

                // Set the location hash.
                if (url && !settings.fromHashChange) {

                    // rebuilding the hash here since we loose it earlier on
                    // TODO preserve the originally passed in path
                    if (!$.mobile.path.isPath(url) && url.indexOf("#") < 0) {
                        url = "#" + url;
                    }

                    // TODO the property names here are just silly
                    params = {
                        transition: settings.transition,
                        title: pageTitle,
                        pageUrl: pageUrl,
                        role: settings.role
                    };

                    if (settings.changeHash !== false && $.mobile.hashListeningEnabled) {
                        $.mobile.navigate(this.window[0].encodeURI(url), params, true);
                    } else if (toPage[0] !== $.mobile.firstPage[0]) {
                        $.mobile.navigate.history.add(url, params);
                    }
                }

                //set page title
                document.title = pageTitle;

                //set "toPage" as activePage deprecated in 1.4 remove in 1.5
                $.mobile.activePage = toPage;

                //new way to handle activePage
                this.activePage = toPage;

                // If we're navigating back in the URL history, set reverse accordingly.
                settings.reverse = settings.reverse || historyDir < 0;

                cssTransitionDeferred = $.Deferred();

                this._cssTransition(toPage, fromPage, {
                    transition: settings.transition,
                    reverse: settings.reverse,
                    deferred: cssTransitionDeferred
                });

                cssTransitionDeferred.done($.proxy(function (name, reverse, $to, $from, alreadyFocused) {
                    $.mobile.removeActiveLinkClass();

                    //if there's a duplicateCachedPage, remove it from the DOM now that it's hidden
                    if (settings.duplicateCachedPage) {
                        settings.duplicateCachedPage.remove();
                    }

                    // despite visibility: hidden addresses issue #2965
                    // https://github.com/jquery/jquery-mobile/issues/2965
                    if (!alreadyFocused) {
                        $.mobile.focusPage(toPage);
                    }

                    this._releaseTransitionLock();
                    this._triggerWithDeprecated("transition", triggerData);
                    this._triggerWithDeprecated("change", triggerData);
                }, this));
            },

            // determine the current base url
            _findBaseWithDefault: function () {
                var closestBase = (this.activePage &&
                $.mobile.getClosestBaseUrl(this.activePage));
                return closestBase || $.mobile.path.documentBase.hrefNoHash;
            }
        });

        // The following handlers should be bound after mobileinit has been triggered
        // the following deferred is resolved in the init file
        $.mobile.navreadyDeferred = $.Deferred();

        //these variables make all page containers use the same queue and only navigate one at a time
        // queue to hold simultanious page transitions
        var pageTransitionQueue = [],

            // indicates whether or not page is in process of transitioning
            isPageTransitioning = false;

    })(jQuery);

    (function ($, undefined) {

        // resolved on domready
        var domreadyDeferred = $.Deferred(),

            // resolved and nulled on window.load()
            loadDeferred = $.Deferred(),

            // function that resolves the above deferred
            pageIsFullyLoaded = function () {

                // Resolve and null the deferred
                loadDeferred.resolve();
                loadDeferred = null;
            },

            documentUrl = $.mobile.path.documentUrl,

            // used to track last vclicked element to make sure its value is added to form data
            $lastVClicked = null;

        /* Event Bindings - hashchange, submit, and click */
        function findClosestLink(ele) {
            while (ele) {
                // Look for the closest element with a nodeName of "a".
                // Note that we are checking if we have a valid nodeName
                // before attempting to access it. This is because the
                // node we get called with could have originated from within
                // an embedded SVG document where some symbol instance elements
                // don't have nodeName defined on them, or strings are of type
                // SVGAnimatedString.
                if ((typeof ele.nodeName === "string") && ele.nodeName.toLowerCase() === "a") {
                    break;
                }
                ele = ele.parentNode;
            }
            return ele;
        }

        $.mobile.loadPage = function (url, opts) {
            var container;

            opts = opts || {};
            container = (opts.pageContainer || $.mobile.pageContainer);

            // create the deferred that will be supplied to loadPage callers
            // and resolved by the content widget's load method
            opts.deferred = $.Deferred();

            // Preferring to allow exceptions for uninitialized opts.pageContainer
            // widgets so we know if we need to force init here for users
            container.pagecontainer("load", url, opts);

            // provide the deferred
            return opts.deferred.promise();
        };

        //define vars for interal use

        /* internal utility functions */

        // NOTE Issue #4950 Android phonegap doesn't navigate back properly
        //      when a full page refresh has taken place. It appears that hashchange
        //      and replacestate history alterations work fine but we need to support
        //      both forms of history traversal in our code that uses backward history
        //      movement
        $.mobile.back = function () {
            var nav = window.navigator;

            // if the setting is on and the navigator object is
            // available use the phonegap navigation capability
            if (this.phonegapNavigationEnabled &&
                nav &&
                nav.app &&
                nav.app.backHistory) {
                nav.app.backHistory();
            } else {
                $.mobile.pageContainer.pagecontainer("back");
            }
        };

        // Direct focus to the page title, or otherwise first focusable element
        $.mobile.focusPage = function (page) {
            var autofocus = page.find("[autofocus]"),
                pageTitle = page.find(".ui-title:eq(0)");

            if (autofocus.length) {
                autofocus.focus();
                return;
            }

            if (pageTitle.length) {
                pageTitle.focus();
            } else {
                page.focus();
            }
        };

        // No-op implementation of transition degradation
        $.mobile._maybeDegradeTransition = $.mobile._maybeDegradeTransition || function (transition) {
            return transition;
        };

        // Exposed $.mobile methods

        $.mobile.changePage = function (to, options) {
            $.mobile.pageContainer.pagecontainer("change", to, options);
        };

        $.mobile.changePage.defaults = {
            transition: undefined,
            reverse: false,
            changeHash: true,
            fromHashChange: false,
            role: undefined, // By default we rely on the role defined by the @data-role attribute.
            duplicateCachedPage: undefined,
            pageContainer: undefined,
            showLoadMsg: true, //loading message shows by default when pages are being fetched during changePage
            dataUrl: undefined,
            fromPage: undefined,
            allowSamePageTransition: false
        };

        $.mobile._registerInternalEvents = function () {
            var getAjaxFormData = function ($form, calculateOnly) {
                var url, ret = true, formData, vclickedName, method;
                if (!$.mobile.ajaxEnabled ||
                    // test that the form is, itself, ajax false
                        $form.is(":jqmData(ajax='false')") ||
                    // test that $.mobile.ignoreContentEnabled is set and
                    // the form or one of it's parents is ajax=false
                        !$form.jqmHijackable().length ||
                        $form.attr("target")) {
                    return false;
                }

                url = ($lastVClicked && $lastVClicked.attr("formaction")) ||
                    $form.attr("action");
                method = ($form.attr("method") || "get").toLowerCase();

                // If no action is specified, browsers default to using the
                // URL of the document containing the form. Since we dynamically
                // pull in pages from external documents, the form should submit
                // to the URL for the source document of the page containing
                // the form.
                if (!url) {
                    // Get the @data-url for the page containing the form.
                    url = $.mobile.getClosestBaseUrl($form);

                    // NOTE: If the method is "get", we need to strip off the query string
                    // because it will get replaced with the new form data. See issue #5710.
                    if (method === "get") {
                        url = $.mobile.path.parseUrl(url).hrefNoSearch;
                    }

                    if (url === $.mobile.path.documentBase.hrefNoHash) {
                        // The url we got back matches the document base,
                        // which means the page must be an internal/embedded page,
                        // so default to using the actual document url as a browser
                        // would.
                        url = documentUrl.hrefNoSearch;
                    }
                }

                url = $.mobile.path.makeUrlAbsolute(url, $.mobile.getClosestBaseUrl($form));

                if (($.mobile.path.isExternal(url) && !$.mobile.path.isPermittedCrossDomainRequest(documentUrl, url))) {
                    return false;
                }

                if (!calculateOnly) {
                    formData = $form.serializeArray();

                    if ($lastVClicked && $lastVClicked[0].form === $form[0]) {
                        vclickedName = $lastVClicked.attr("name");
                        if (vclickedName) {
                            // Make sure the last clicked element is included in the form
                            $.each(formData, function (key, value) {
                                if (value.name === vclickedName) {
                                    // Unset vclickedName - we've found it in the serialized data already
                                    vclickedName = "";
                                    return false;
                                }
                            });
                            if (vclickedName) {
                                formData.push({ name: vclickedName, value: $lastVClicked.attr("value") });
                            }
                        }
                    }

                    ret = {
                        url: url,
                        options: {
                            type: method,
                            data: $.param(formData),
                            transition: $form.jqmData("transition"),
                            reverse: $form.jqmData("direction") === "reverse",
                            reloadPage: true
                        }
                    };
                }

                return ret;
            };

            //bind to form submit events, handle with Ajax
            $.mobile.document.delegate("form", "submit", function (event) {
                var formData;

                if (!event.isDefaultPrevented()) {
                    formData = getAjaxFormData($(this));
                    if (formData) {
                        $.mobile.changePage(formData.url, formData.options);
                        event.preventDefault();
                    }
                }
            });

            //add active state on vclick
            $.mobile.document.bind("vclick", function (event) {
                var $btn, btnEls, target = event.target, needClosest = false;
                // if this isn't a left click we don't care. Its important to note
                // that when the virtual event is generated it will create the which attr
                if (event.which > 1 || !$.mobile.linkBindingEnabled) {
                    return;
                }

                // Record that this element was clicked, in case we need it for correct
                // form submission during the "submit" handler above
                $lastVClicked = $(target);

                // Try to find a target element to which the active class will be applied
                if ($.data(target, "mobile-button")) {
                    // If the form will not be submitted via AJAX, do not add active class
                    if (!getAjaxFormData($(target).closest("form"), true)) {
                        return;
                    }
                    // We will apply the active state to this button widget - the parent
                    // of the input that was clicked will have the associated data
                    if (target.parentNode) {
                        target = target.parentNode;
                    }
                } else {
                    target = findClosestLink(target);
                    if (!(target && $.mobile.path.parseUrl(target.getAttribute("href") || "#").hash !== "#")) {
                        return;
                    }

                    // TODO teach $.mobile.hijackable to operate on raw dom elements so the
                    // link wrapping can be avoided
                    if (!$(target).jqmHijackable().length) {
                        return;
                    }
                }

                // Avoid calling .closest by using the data set during .buttonMarkup()
                // List items have the button data in the parent of the element clicked
                if (!!~target.className.indexOf("ui-link-inherit")) {
                    if (target.parentNode) {
                        btnEls = $.data(target.parentNode, "buttonElements");
                    }
                    // Otherwise, look for the data on the target itself
                } else {
                    btnEls = $.data(target, "buttonElements");
                }
                // If found, grab the button's outer element
                if (btnEls) {
                    target = btnEls.outer;
                } else {
                    needClosest = true;
                }

                $btn = $(target);
                // If the outer element wasn't found by the our heuristics, use .closest()
                if (needClosest) {
                    $btn = $btn.closest(".ui-btn");
                }

                if ($btn.length > 0 &&
                    !($btn.hasClass("ui-state-disabled" ||

                    // DEPRECATED as of 1.4.0 - remove after 1.4.0 release
                    // only ui-state-disabled should be present thereafter
                        $btn.hasClass("ui-disabled")))) {
                    $.mobile.removeActiveLinkClass(true);
                    $.mobile.activeClickedLink = $btn;
                    $.mobile.activeClickedLink.addClass($.mobile.activeBtnClass);
                }
            });

            // click routing - direct to HTTP or Ajax, accordingly
            $.mobile.document.bind("click", function (event) {
                if (!$.mobile.linkBindingEnabled || event.isDefaultPrevented()) {
                    return;
                }

                var link = findClosestLink(event.target),
                    $link = $(link),

                    //remove active link class if external (then it won't be there if you come back)
                    httpCleanup = function () {
                        window.setTimeout(function () { $.mobile.removeActiveLinkClass(true); }, 200);
                    },
                    baseUrl, href,
                    useDefaultUrlHandling, isExternal,
                    transition, reverse, role;

                // If a button was clicked, clean up the active class added by vclick above
                if ($.mobile.activeClickedLink &&
                    $.mobile.activeClickedLink[0] === event.target.parentNode) {
                    httpCleanup();
                }

                // If there is no link associated with the click or its not a left
                // click we want to ignore the click
                // TODO teach $.mobile.hijackable to operate on raw dom elements so the link wrapping
                // can be avoided
                if (!link || event.which > 1 || !$link.jqmHijackable().length) {
                    return;
                }

                //if there's a data-rel=back attr, go back in history
                if ($link.is(":jqmData(rel='back')")) {
                    $.mobile.back();
                    return false;
                }

                baseUrl = $.mobile.getClosestBaseUrl($link);

                //get href, if defined, otherwise default to empty hash
                href = $.mobile.path.makeUrlAbsolute($link.attr("href") || "#", baseUrl);

                //if ajax is disabled, exit early
                if (!$.mobile.ajaxEnabled && !$.mobile.path.isEmbeddedPage(href)) {
                    httpCleanup();
                    //use default click handling
                    return;
                }

                // XXX_jblas: Ideally links to application pages should be specified as
                //            an url to the application document with a hash that is either
                //            the site relative path or id to the page. But some of the
                //            internal code that dynamically generates sub-pages for nested
                //            lists and select dialogs, just write a hash in the link they
                //            create. This means the actual URL path is based on whatever
                //            the current value of the base tag is at the time this code
                //            is called.
                if (href.search("#") !== -1 &&
                    !($.mobile.path.isExternal(href) && $.mobile.path.isAbsoluteUrl(href))) {

                    href = href.replace(/[^#]*#/, "");
                    if (!href) {
                        //link was an empty hash meant purely
                        //for interaction, so we ignore it.
                        event.preventDefault();
                        return;
                    } else if ($.mobile.path.isPath(href)) {
                        //we have apath so make it the href we want to load.
                        href = $.mobile.path.makeUrlAbsolute(href, baseUrl);
                    } else {
                        //we have a simple id so use the documentUrl as its base.
                        href = $.mobile.path.makeUrlAbsolute("#" + href, documentUrl.hrefNoHash);
                    }
                }

                // Should we handle this link, or let the browser deal with it?
                useDefaultUrlHandling = $link.is("[rel='external']") || $link.is(":jqmData(ajax='false')") || $link.is("[target]");

                // Some embedded browsers, like the web view in Phone Gap, allow cross-domain XHR
                // requests if the document doing the request was loaded via the file:// protocol.
                // This is usually to allow the application to "phone home" and fetch app specific
                // data. We normally let the browser handle external/cross-domain urls, but if the
                // allowCrossDomainPages option is true, we will allow cross-domain http/https
                // requests to go through our page loading logic.

                //check for protocol or rel and its not an embedded page
                //TODO overlap in logic from isExternal, rel=external check should be
                //     moved into more comprehensive isExternalLink
                isExternal = useDefaultUrlHandling || ($.mobile.path.isExternal(href) && !$.mobile.path.isPermittedCrossDomainRequest(documentUrl, href));

                if (isExternal) {
                    httpCleanup();
                    //use default click handling
                    return;
                }

                //use ajax
                transition = $link.jqmData("transition");
                reverse = $link.jqmData("direction") === "reverse" ||
                            // deprecated - remove by 1.0
                            $link.jqmData("back");

                //this may need to be more specific as we use data-rel more
                role = $link.attr("data-" + $.mobile.ns + "rel") || undefined;

                $.mobile.changePage(href, { transition: transition, reverse: reverse, role: role, link: $link });
                event.preventDefault();
            });

            //prefetch pages when anchors with data-prefetch are encountered
            $.mobile.document.delegate(".ui-page", "pageshow.prefetch", function () {
                var urls = [];
                $(this).find("a:jqmData(prefetch)").each(function () {
                    var $link = $(this),
                        url = $link.attr("href");

                    if (url && $.inArray(url, urls) === -1) {
                        urls.push(url);

                        $.mobile.loadPage(url, { role: $link.attr("data-" + $.mobile.ns + "rel"), prefetch: true });
                    }
                });
            });

            // TODO ensure that the navigate binding in the content widget happens at the right time
            $.mobile.pageContainer.pagecontainer();

            //set page min-heights to be device specific
            $.mobile.document.bind("pageshow", function () {

                // We need to wait for window.load to make sure that styles have already been rendered,
                // otherwise heights of external toolbars will have the wrong value
                if (loadDeferred) {
                    loadDeferred.done($.mobile.resetActivePageHeight);
                } else {
                    $.mobile.resetActivePageHeight();
                }
            });
            $.mobile.window.bind("throttledresize", $.mobile.resetActivePageHeight);

        };//navreadyDeferred done callback

        $(function () { domreadyDeferred.resolve(); });

        // Account for the possibility that the load event has already fired
        if (document.readyState === "complete") {
            pageIsFullyLoaded();
        } else {
            $.mobile.window.load(pageIsFullyLoaded);
        }

        $.when(domreadyDeferred, $.mobile.navreadyDeferred).done(function () { $.mobile._registerInternalEvents(); });
    })(jQuery);


    (function ($) {
        // TODO move loader class down into the widget settings
        var loaderClass = "ui-loader", $html = $("html");

        $.widget("mobile.loader", {
            // NOTE if the global config settings are defined they will override these
            //      options
            options: {
                // the theme for the loading message
                theme: "a",

                // whether the text in the loading message is shown
                textVisible: false,

                // custom html for the inner content of the loading message
                html: "",

                // the text to be displayed when the popup is shown
                text: "loading"
            },

            defaultHtml: "<div class='" + loaderClass + "'>" +
                "<span class='ui-icon-loading'></span>" +
                "<h1></h1>" +
                "</div>",

            // For non-fixed supportin browsers. Position at y center (if scrollTop supported), above the activeBtn (if defined), or just 100px from top
            fakeFixLoader: function () {
                var activeBtn = $("." + $.mobile.activeBtnClass).first();

                this.element
                    .css({
                        top: $.support.scrollTop && this.window.scrollTop() + this.window.height() / 2 ||
                            activeBtn.length && activeBtn.offset().top || 100
                    });
            },

            // check position of loader to see if it appears to be "fixed" to center
            // if not, use abs positioning
            checkLoaderPosition: function () {
                var offset = this.element.offset(),
                    scrollTop = this.window.scrollTop(),
                    screenHeight = $.mobile.getScreenHeight();

                if (offset.top < scrollTop || (offset.top - scrollTop) > screenHeight) {
                    this.element.addClass("ui-loader-fakefix");
                    this.fakeFixLoader();
                    this.window
                        .unbind("scroll", this.checkLoaderPosition)
                        .bind("scroll", $.proxy(this.fakeFixLoader, this));
                }
            },

            resetHtml: function () {
                this.element.html($(this.defaultHtml).html());
            },

            // Turn on/off page loading message. Theme doubles as an object argument
            // with the following shape: { theme: '', text: '', html: '', textVisible: '' }
            // NOTE that the $.mobile.loading* settings and params past the first are deprecated
            // TODO sweet jesus we need to break some of this out
            show: function (theme, msgText, textonly) {
                var textVisible, message, loadSettings;

                this.resetHtml();

                // use the prototype options so that people can set them globally at
                // mobile init. Consistency, it's what's for dinner
                if ($.type(theme) === "object") {
                    loadSettings = $.extend({}, this.options, theme);

                    theme = loadSettings.theme;
                } else {
                    loadSettings = this.options;

                    // here we prefer the theme value passed as a string argument, then
                    // we prefer the global option because we can't use undefined default
                    // prototype options, then the prototype option
                    theme = theme || loadSettings.theme;
                }

                // set the message text, prefer the param, then the settings object
                // then loading message
                message = msgText || (loadSettings.text === false ? "" : loadSettings.text);

                // prepare the dom
                $html.addClass("ui-loading");

                textVisible = loadSettings.textVisible;

                // add the proper css given the options (theme, text, etc)
                // Force text visibility if the second argument was supplied, or
                // if the text was explicitly set in the object args
                this.element.attr("class", loaderClass +
                    " ui-corner-all ui-body-" + theme +
                    " ui-loader-" + (textVisible || msgText || theme.text ? "verbose" : "default") +
                    (loadSettings.textonly || textonly ? " ui-loader-textonly" : ""));

                // TODO verify that jquery.fn.html is ok to use in both cases here
                //      this might be overly defensive in preventing unknowing xss
                // if the html attribute is defined on the loading settings, use that
                // otherwise use the fallbacks from above
                if (loadSettings.html) {
                    this.element.html(loadSettings.html);
                } else {
                    this.element.find("h1").text(message);
                }

                // If the pagecontainer widget has been defined we may use the :mobile-pagecontainer
                // and attach to the element on which the pagecontainer widget has been defined. If not,
                // we attach to the body.
                this.element.appendTo($.mobile.pagecontainer ?
                    $(":mobile-pagecontainer") : $("body"));

                // check that the loader is visible
                this.checkLoaderPosition();

                // on scroll check the loader position
                this.window.bind("scroll", $.proxy(this.checkLoaderPosition, this));
            },

            hide: function () {
                $html.removeClass("ui-loading");

                if (this.options.text) {
                    this.element.removeClass("ui-loader-fakefix");
                }

                this.window.unbind("scroll", this.fakeFixLoader);
                this.window.unbind("scroll", this.checkLoaderPosition);
            }
        });

    })(jQuery, this);


    (function ($, window, undefined) {
        var $html = $("html"),
            $window = $.mobile.window;

        //remove initial build class (only present on first pageshow)
        function hideRenderingClass() {
            $html.removeClass("ui-mobile-rendering");
        }

        // trigger mobileinit event - useful hook for configuring $.mobile settings before they're used
        $(window.document).trigger("mobileinit");

        // support conditions
        // if device support condition(s) aren't met, leave things as they are -> a basic, usable experience,
        // otherwise, proceed with the enhancements
        if (!$.mobile.gradeA()) {
            return;
        }

        // override ajaxEnabled on platforms that have known conflicts with hash history updates
        // or generally work better browsing in regular http for full page refreshes (BB5, Opera Mini)
        if ($.mobile.ajaxBlacklist) {
            $.mobile.ajaxEnabled = false;
        }

        // Add mobile, initial load "rendering" classes to docEl
        $html.addClass("ui-mobile ui-mobile-rendering");

        // This is a fallback. If anything goes wrong (JS errors, etc), or events don't fire,
        // this ensures the rendering class is removed after 5 seconds, so content is visible and accessible
        setTimeout(hideRenderingClass, 5000);

        $.extend($.mobile, {
            // find and enhance the pages in the dom and transition to the first page.
            initializePage: function () {
                // find present pages
                var path = $.mobile.path,
                    $pages = $(":jqmData(role='page'), :jqmData(role='dialog')"),
                    hash = path.stripHash(path.stripQueryParams(path.parseLocation().hash)),
                    theLocation = $.mobile.path.parseLocation(),
                    hashPage = hash ? document.getElementById(hash) : undefined;

                // if no pages are found, create one with body's inner html
                if (!$pages.length) {
                    $pages = $("body").wrapInner("<div data-" + $.mobile.ns + "role='page'></div>").children(0);
                }

                // add dialogs, set data-url attrs
                $pages.each(function () {
                    var $this = $(this);

                    // unless the data url is already set set it to the pathname
                    if (!$this[0].getAttribute("data-" + $.mobile.ns + "url")) {
                        $this.attr("data-" + $.mobile.ns + "url", $this.attr("id") ||
                            path.convertUrlToDataUrl(theLocation.pathname + theLocation.search));
                    }
                });

                // define first page in dom case one backs out to the directory root (not always the first page visited, but defined as fallback)
                $.mobile.firstPage = $pages.first();

                // define page container
                $.mobile.pageContainer = $.mobile.firstPage
                    .parent()
                    .addClass("ui-mobile-viewport")
                    .pagecontainer();

                // initialize navigation events now, after mobileinit has occurred and the page container
                // has been created but before the rest of the library is alerted to that fact
                $.mobile.navreadyDeferred.resolve();

                // alert listeners that the pagecontainer has been determined for binding
                // to events triggered on it
                $window.trigger("pagecontainercreate");

                // cue page loading message
                ///$.mobile.loading( "show" );

                //remove initial build class (only present on first pageshow)
                hideRenderingClass();

                // if hashchange listening is disabled, there's no hash deeplink,
                // the hash is not valid (contains more than one # or does not start with #)
                // or there is no page with that hash, change to the first page in the DOM
                // Remember, however, that the hash can also be a path!
                if (!($.mobile.hashListeningEnabled &&
                    $.mobile.path.isHashValid(location.hash) &&
                    ($(hashPage).is(":jqmData(role='page')") ||
                        $.mobile.path.isPath(hash) ||
                        hash === $.mobile.dialogHashKey))) {

                    // make sure to set initial popstate state if it exists
                    // so that navigation back to the initial page works properly
                    if ($.event.special.navigate.isPushStateEnabled()) {
                        $.mobile.navigate.navigator.squash(path.parseLocation().href);
                    }

                    $.mobile.changePage($.mobile.firstPage, {
                        transition: "none",
                        reverse: true,
                        changeHash: false,
                        fromHashChange: true
                    });
                } else {
                    // trigger hashchange or navigate to squash and record the correct
                    // history entry for an initial hash path
                    if (!$.event.special.navigate.isPushStateEnabled()) {
                        $window.trigger("hashchange", [true]);
                    } else {
                        // TODO figure out how to simplify this interaction with the initial history entry
                        // at the bottom js/navigate/navigate.js
                        $.mobile.navigate.history.stack = [];
                        $.mobile.navigate($.mobile.path.isPath(location.hash) ? location.hash : location.href);
                    }
                }
            }
        });

        $(function () {
            //Run inlineSVG support test
            $.support.inlineSVG();

            // check which scrollTop value should be used by scrolling to 1 immediately at domready
            // then check what the scroll top is. Android will report 0... others 1
            // note that this initial scroll won't hide the address bar. It's just for the check.

            // hide iOS browser chrome on load if hideUrlBar is true this is to try and do it as soon as possible
            if ($.mobile.hideUrlBar) {
                window.scrollTo(0, 1);
            }

            // if defaultHomeScroll hasn't been set yet, see if scrollTop is 1
            // it should be 1 in most browsers, but android treats 1 as 0 (for hiding addr bar)
            // so if it's 1, use 0 from now on
            $.mobile.defaultHomeScroll = (!$.support.scrollTop || $.mobile.window.scrollTop() === 1) ? 0 : 1;

            //dom-ready inits
            if ($.mobile.autoInitializePage) {
                $.mobile.initializePage();
            }

            // window load event
            // hide iOS browser chrome on load if hideUrlBar is true this is as fall back incase we were too early before
            if ($.mobile.hideUrlBar) {
                $window.load($.mobile.silentScroll);
            }

            if (!$.support.cssPointerEvents) {
                // IE and Opera don't support CSS pointer-events: none that we use to disable link-based buttons
                // by adding the 'ui-disabled' class to them. Using a JavaScript workaround for those browser.
                // https://github.com/jquery/jquery-mobile/issues/3558

                // DEPRECATED as of 1.4.0 - remove ui-disabled after 1.4.0 release
                // only ui-state-disabled should be present thereafter
                $.mobile.document.delegate(".ui-state-disabled,.ui-disabled", "vclick",
                    function (e) {
                        e.preventDefault();
                        e.stopImmediatePropagation();
                    }
                );
            }
        });
    }(jQuery, this));

    (function ($, undefined) {

        $.mobile.links = function (target) {

            //links within content areas, tests included with page
            $(target)
                .find("a")
                .jqmEnhanceable()
                .filter(":jqmData(rel='popup')[href][href!='']")
                .each(function () {
                    // Accessibility info for popups
                    var element = this,
                        idref = element.getAttribute("href").substring(1);

                    if (idref) {
                        element.setAttribute("aria-haspopup", true);
                        element.setAttribute("aria-owns", idref);
                        element.setAttribute("aria-expanded", false);
                    }
                })
                .end()
                .not(".ui-btn, :jqmData(role='none'), :jqmData(role='nojs')")
                .addClass("ui-link");

        };

    })(jQuery);


    (function ($, undefined) {

        $.mobile.nojs = function (target) {
            $(":jqmData(role='nojs')", target).addClass("ui-nojs");
        };

    })(jQuery);

    (function ($) {
        var meta = $("meta[name=viewport]"),
            initialContent = meta.attr("content"),
            disabledZoom = initialContent + ",maximum-scale=1, user-scalable=no",
            enabledZoom = initialContent + ",maximum-scale=10, user-scalable=yes",
            disabledInitially = /(user-scalable[\s]*=[\s]*no)|(maximum-scale[\s]*=[\s]*1)[$,\s]/.test(initialContent);

        $.mobile.zoom = $.extend({}, {
            enabled: !disabledInitially,
            locked: false,
            disable: function (lock) {
                if (!disabledInitially && !$.mobile.zoom.locked) {
                    meta.attr("content", disabledZoom);
                    $.mobile.zoom.enabled = false;
                    $.mobile.zoom.locked = lock || false;
                }
            },
            enable: function (unlock) {
                if (!disabledInitially && (!$.mobile.zoom.locked || unlock === true)) {
                    meta.attr("content", enabledZoom);
                    $.mobile.zoom.enabled = true;
                    $.mobile.zoom.locked = false;
                }
            },
            restore: function () {
                if (!disabledInitially) {
                    meta.attr("content", initialContent);
                    $.mobile.zoom.enabled = true;
                }
            }
        });

    }(jQuery));

    (function ($, window) {

        $.mobile.iosorientationfixEnabled = true;

        // This fix addresses an iOS bug, so return early if the UA claims it's something else.
        var ua = navigator.userAgent,
            zoom,
            evt, x, y, z, aig;
        if (!(/iPhone|iPad|iPod/.test(navigator.platform) && /OS [1-5]_[0-9_]* like Mac OS X/i.test(ua) && ua.indexOf("AppleWebKit") > -1)) {
            $.mobile.iosorientationfixEnabled = false;
            return;
        }

        zoom = $.mobile.zoom;

        function checkTilt(e) {
            evt = e.originalEvent;
            aig = evt.accelerationIncludingGravity;

            x = Math.abs(aig.x);
            y = Math.abs(aig.y);
            z = Math.abs(aig.z);

            // If portrait orientation and in one of the danger zones
            if (!window.orientation && (x > 7 || ((z > 6 && y < 8 || z < 8 && y > 6) && x > 5))) {
                if (zoom.enabled) {
                    zoom.disable();
                }
            } else if (!zoom.enabled) {
                zoom.enable();
            }
        }

        $.mobile.document.on("mobileinit", function () {
            if ($.mobile.iosorientationfixEnabled) {
                $.mobile.window
                    .bind("orientationchange.iosorientationfix", zoom.enable)
                    .bind("devicemotion.iosorientationfix", checkTilt);
            }
        });

    }(jQuery, this));

    /*
    * fallback transition for flip in non-3D supporting browsers (which tend to handle complex transitions poorly in general
    */

    (function ($, window, undefined) {

        $.mobile.transitionFallbacks.flip = "fade";

    })(jQuery, this);

    /*
    * fallback transition for flow in non-3D supporting browsers (which tend to handle complex transitions poorly in general
    */

    (function ($, window, undefined) {

        $.mobile.transitionFallbacks.flow = "fade";

    })(jQuery, this);

    /*
    * fallback transition for pop in non-3D supporting browsers (which tend to handle complex transitions poorly in general
    */

    (function ($, window, undefined) {

        $.mobile.transitionFallbacks.pop = "fade";

    })(jQuery, this);

    /*
    * fallback transition for slide in non-3D supporting browsers (which tend to handle complex transitions poorly in general
    */

    (function ($, window, undefined) {

        // Use the simultaneous transitions handler for slide transitions
        $.mobile.transitionHandlers.slide = $.mobile.transitionHandlers.simultaneous;

        // Set the slide transitions's fallback to "fade"
        $.mobile.transitionFallbacks.slide = "fade";

    })(jQuery, this);

    /*
    * fallback transition for slidedown in non-3D supporting browsers (which tend to handle complex transitions poorly in general
    */

    (function ($, window, undefined) {

        $.mobile.transitionFallbacks.slidedown = "fade";

    })(jQuery, this);

    /*
    * fallback transition for slidefade in non-3D supporting browsers (which tend to handle complex transitions poorly in general
    */

    (function ($, window, undefined) {

        // Set the slide transitions's fallback to "fade"
        $.mobile.transitionFallbacks.slidefade = "fade";

    })(jQuery, this);

    /*
    * fallback transition for slideup in non-3D supporting browsers (which tend to handle complex transitions poorly in general
    */

    (function ($, window, undefined) {

        $.mobile.transitionFallbacks.slideup = "fade";

    })(jQuery, this);

    /*
    * fallback transition for turn in non-3D supporting browsers (which tend to handle complex transitions poorly in general
    */

    (function ($, window, undefined) {

        $.mobile.transitionFallbacks.turn = "fade";

    })(jQuery, this);


    (function ($, undefined) {

        var uiScreenHiddenRegex = /\bui-screen-hidden\b/;
        function noHiddenClass(elements) {
            var index,
                length = elements.length,
                result = [];

            for (index = 0; index < length; index++) {
                if (!elements[index].className.match(uiScreenHiddenRegex)) {
                    result.push(elements[index]);
                }
            }

            return $(result);
        }

        $.mobile.behaviors.addFirstLastClasses = {
            _getVisibles: function ($els, create) {
                var visibles;

                if (create) {
                    visibles = noHiddenClass($els);
                } else {
                    visibles = $els.filter(":visible");
                    if (visibles.length === 0) {
                        visibles = noHiddenClass($els);
                    }
                }

                return visibles;
            },

            _addFirstLastClasses: function ($els, $visibles, create) {
                $els.removeClass("ui-first-child ui-last-child");
                $visibles.eq(0).addClass("ui-first-child").end().last().addClass("ui-last-child");
                if (!create) {
                    this.element.trigger("updatelayout");
                }
            },

            _removeFirstLastClasses: function ($els) {
                $els.removeClass("ui-first-child ui-last-child");
            }
        };

    })(jQuery);

    (function ($, undefined) {

        var rInitialLetter = /([A-Z])/g,

            // Construct iconpos class from iconpos value
            iconposClass = function (iconpos) {
                return ("ui-btn-icon-" + (iconpos === null ? "left" : iconpos));
            };

        $.widget("mobile.collapsible", {
            options: {
                enhanced: false,
                expandCueText: null,
                collapseCueText: null,
                collapsed: true,
                heading: "h1,h2,h3,h4,h5,h6,legend",
                collapsedIcon: null,
                expandedIcon: null,
                iconpos: null,
                theme: null,
                contentTheme: null,
                inset: null,
                corners: null,
                mini: null
            },

            _create: function () {
                var elem = this.element,
                    ui = {
                        accordion: elem
                            .closest(":jqmData(role='collapsible-set')," +
                                ":jqmData(role='collapsibleset')" +
                                ($.mobile.collapsibleset ? ", :mobile-collapsibleset" :
                                    ""))
                            .addClass("ui-collapsible-set")
                    };

                this._ui = ui;
                this._renderedOptions = this._getOptions(this.options);

                if (this.options.enhanced) {
                    ui.heading = this.element.children(".ui-collapsible-heading");
                    ui.content = ui.heading.next();
                    ui.anchor = ui.heading.children();
                    ui.status = ui.anchor.children(".ui-collapsible-heading-status");
                } else {
                    this._enhance(elem, ui);
                }

                this._on(ui.heading, {
                    "tap": function () {
                        ui.heading.find("a").first().addClass($.mobile.activeBtnClass);
                    },

                    "click": function (event) {
                        this._handleExpandCollapse(!ui.heading.hasClass("ui-collapsible-heading-collapsed"));
                        event.preventDefault();
                        event.stopPropagation();
                    }
                });
            },

            // Adjust the keys inside options for inherited values
            _getOptions: function (options) {
                var key,
                    accordion = this._ui.accordion,
                    accordionWidget = this._ui.accordionWidget;

                // Copy options
                options = $.extend({}, options);

                if (accordion.length && !accordionWidget) {
                    this._ui.accordionWidget =
                    accordionWidget = accordion.data("mobile-collapsibleset");
                }

                for (key in options) {

                    // Retrieve the option value first from the options object passed in and, if
                    // null, from the parent accordion or, if that's null too, or if there's no
                    // parent accordion, then from the defaults.
                    options[key] =
                        (options[key] != null) ? options[key] :
                        (accordionWidget) ? accordionWidget.options[key] :
                        accordion.length ? $.mobile.getAttribute(accordion[0],
                            key.replace(rInitialLetter, "-$1").toLowerCase()) :
                        null;

                    if (null == options[key]) {
                        options[key] = $.mobile.collapsible.defaults[key];
                    }
                }

                return options;
            },

            _themeClassFromOption: function (prefix, value) {
                return (value ? (value === "none" ? "" : prefix + value) : "");
            },

            _enhance: function (elem, ui) {
                var iconclass,
                    opts = this._renderedOptions,
                    contentThemeClass = this._themeClassFromOption("ui-body-", opts.contentTheme);

                elem.addClass("ui-collapsible " +
                    (opts.inset ? "ui-collapsible-inset " : "") +
                    (opts.inset && opts.corners ? "ui-corner-all " : "") +
                    (contentThemeClass ? "ui-collapsible-themed-content " : ""));
                ui.originalHeading = elem.children(this.options.heading).first(),
                ui.content = elem
                    .wrapInner("<div " +
                        "class='ui-collapsible-content " +
                        contentThemeClass + "'></div>")
                    .children(".ui-collapsible-content"),
                ui.heading = ui.originalHeading;

                // Replace collapsibleHeading if it's a legend
                if (ui.heading.is("legend")) {
                    ui.heading = $("<div role='heading'>" + ui.heading.html() + "</div>");
                    ui.placeholder = $("<div><!-- placeholder for legend --></div>").insertBefore(ui.originalHeading);
                    ui.originalHeading.remove();
                }

                iconclass = (opts.collapsed ? (opts.collapsedIcon ? "ui-icon-" + opts.collapsedIcon : "") :
                    (opts.expandedIcon ? "ui-icon-" + opts.expandedIcon : ""));

                ui.status = $("<span class='ui-collapsible-heading-status'></span>");
                ui.anchor = ui.heading
                    .detach()
                    //modify markup & attributes
                    .addClass("ui-collapsible-heading")
                    .append(ui.status)
                    .wrapInner("<a href='#' class='ui-collapsible-heading-toggle'></a>")
                    .find("a")
                        .first()
                        .addClass("ui-btn " +
                            (iconclass ? iconclass + " " : "") +
                            (iconclass ? iconposClass(opts.iconpos) +
                                " " : "") +
                            this._themeClassFromOption("ui-btn-", opts.theme) + " " +
                            (opts.mini ? "ui-mini " : ""));

                //drop heading in before content
                ui.heading.insertBefore(ui.content);

                this._handleExpandCollapse(this.options.collapsed);

                return ui;
            },

            refresh: function () {
                this._applyOptions(this.options);
                this._renderedOptions = this._getOptions(this.options);
            },

            _applyOptions: function (options) {
                var isCollapsed, newTheme, oldTheme, hasCorners, hasIcon,
                    elem = this.element,
                    currentOpts = this._renderedOptions,
                    ui = this._ui,
                    anchor = ui.anchor,
                    status = ui.status,
                    opts = this._getOptions(options);

                // First and foremost we need to make sure the collapsible is in the proper
                // state, in case somebody decided to change the collapsed option at the
                // same time as another option
                if (options.collapsed !== undefined) {
                    this._handleExpandCollapse(options.collapsed);
                }

                isCollapsed = elem.hasClass("ui-collapsible-collapsed");

                // We only need to apply the cue text for the current state right away.
                // The cue text for the alternate state will be stored in the options
                // and applied the next time the collapsible's state is toggled
                if (isCollapsed) {
                    if (opts.expandCueText !== undefined) {
                        status.text(opts.expandCueText);
                    }
                } else {
                    if (opts.collapseCueText !== undefined) {
                        status.text(opts.collapseCueText);
                    }
                }

                // Update icon

                // Is it supposed to have an icon?
                hasIcon =

                    // If the collapsedIcon is being set, consult that
                    (opts.collapsedIcon !== undefined ? opts.collapsedIcon !== false :

                        // Otherwise consult the existing option value
                        currentOpts.collapsedIcon !== false);


                // If any icon-related options have changed, make sure the new icon
                // state is reflected by first removing all icon-related classes
                // reflecting the current state and then adding all icon-related
                // classes for the new state
                if (!(opts.iconpos === undefined &&
                    opts.collapsedIcon === undefined &&
                    opts.expandedIcon === undefined)) {

                    // Remove all current icon-related classes
                    anchor.removeClass([iconposClass(currentOpts.iconpos)]
                        .concat((currentOpts.expandedIcon ?
                            ["ui-icon-" + currentOpts.expandedIcon] : []))
                        .concat((currentOpts.collapsedIcon ?
                            ["ui-icon-" + currentOpts.collapsedIcon] : []))
                        .join(" "));

                    // Add new classes if an icon is supposed to be present
                    if (hasIcon) {
                        anchor.addClass(
                            [iconposClass(opts.iconpos !== undefined ?
                                opts.iconpos : currentOpts.iconpos)]
                                .concat(isCollapsed ?
                                    ["ui-icon-" + (opts.collapsedIcon !== undefined ?
                                        opts.collapsedIcon :
                                        currentOpts.collapsedIcon)] :
                                    ["ui-icon-" + (opts.expandedIcon !== undefined ?
                                        opts.expandedIcon :
                                        currentOpts.expandedIcon)])
                                .join(" "));
                    }
                }

                if (opts.theme !== undefined) {
                    oldTheme = this._themeClassFromOption("ui-btn-", currentOpts.theme);
                    newTheme = this._themeClassFromOption("ui-btn-", opts.theme);
                    anchor.removeClass(oldTheme).addClass(newTheme);
                }

                if (opts.contentTheme !== undefined) {
                    oldTheme = this._themeClassFromOption("ui-body-",
                        currentOpts.contentTheme);
                    newTheme = this._themeClassFromOption("ui-body-",
                        opts.contentTheme);
                    ui.content.removeClass(oldTheme).addClass(newTheme);
                }

                if (opts.inset !== undefined) {
                    elem.toggleClass("ui-collapsible-inset", opts.inset);
                    hasCorners = !!(opts.inset && (opts.corners || currentOpts.corners));
                }

                if (opts.corners !== undefined) {
                    hasCorners = !!(opts.corners && (opts.inset || currentOpts.inset));
                }

                if (hasCorners !== undefined) {
                    elem.toggleClass("ui-corner-all", hasCorners);
                }

                if (opts.mini !== undefined) {
                    anchor.toggleClass("ui-mini", opts.mini);
                }
            },

            _setOptions: function (options) {
                this._applyOptions(options);
                this._super(options);
                this._renderedOptions = this._getOptions(this.options);
            },

            _handleExpandCollapse: function (isCollapse) {
                var opts = this._renderedOptions,
                    ui = this._ui;

                ui.status.text(isCollapse ? opts.expandCueText : opts.collapseCueText);
                ui.heading
                    .toggleClass("ui-collapsible-heading-collapsed", isCollapse)
                    .find("a").first()
                    .toggleClass("ui-icon-" + opts.expandedIcon, !isCollapse)

                    // logic or cause same icon for expanded/collapsed state would remove the ui-icon-class
                    .toggleClass("ui-icon-" + opts.collapsedIcon, (isCollapse || opts.expandedIcon === opts.collapsedIcon))
                    .removeClass($.mobile.activeBtnClass);

                this.element.toggleClass("ui-collapsible-collapsed", isCollapse);
                ui.content
                    .toggleClass("ui-collapsible-content-collapsed", isCollapse)
                    .attr("aria-hidden", isCollapse)
                    .trigger("updatelayout");
                this.options.collapsed = isCollapse;
                this._trigger(isCollapse ? "collapse" : "expand");
            },

            expand: function () {
                this._handleExpandCollapse(false);
            },

            collapse: function () {
                this._handleExpandCollapse(true);
            },

            _destroy: function () {
                var ui = this._ui,
                    opts = this.options;

                if (opts.enhanced) {
                    return;
                }

                if (ui.placeholder) {
                    ui.originalHeading.insertBefore(ui.placeholder);
                    ui.placeholder.remove();
                    ui.heading.remove();
                } else {
                    ui.status.remove();
                    ui.heading
                        .removeClass("ui-collapsible-heading ui-collapsible-heading-collapsed")
                        .children()
                            .contents()
                                .unwrap();
                }

                ui.anchor.contents().unwrap();
                ui.content.contents().unwrap();
                this.element
                    .removeClass("ui-collapsible ui-collapsible-collapsed " +
                        "ui-collapsible-themed-content ui-collapsible-inset ui-corner-all");
            }
        });

        // Defaults to be used by all instances of collapsible if per-instance values
        // are unset or if nothing is specified by way of inheritance from an accordion.
        // Note that this hash does not contain options "collapsed" or "heading",
        // because those are not inheritable.
        $.mobile.collapsible.defaults = {
            expandCueText: " click to expand contents",
            collapseCueText: " click to collapse contents",
            collapsedIcon: "plus",
            contentTheme: "inherit",
            expandedIcon: "minus",
            iconpos: "left",
            inset: true,
            corners: true,
            theme: "inherit",
            mini: false
        };

    })(jQuery);

    (function ($, undefined) {

        var childCollapsiblesSelector = ":mobile-collapsible, " + $.mobile.collapsible.initSelector;

        $.widget("mobile.collapsibleset", $.extend({

            // The initSelector is deprecated as of 1.4.0. In 1.5.0 we will use
            // :jqmData(role='collapsibleset') which will allow us to get rid of the line
            // below altogether, because the autoinit will generate such an initSelector
            initSelector: ":jqmData(role='collapsible-set'),:jqmData(role='collapsibleset')",

            options: $.extend({
                enhanced: false
            }, $.mobile.collapsible.defaults),

            _handleCollapsibleExpand: function (event) {
                var closestCollapsible = $(event.target).closest(".ui-collapsible");

                if (closestCollapsible.parent().is(":mobile-collapsibleset, :jqmData(role='collapsible-set')")) {
                    closestCollapsible
                        .siblings(".ui-collapsible:not(.ui-collapsible-collapsed)")
                        .collapsible("collapse");
                }
            },

            _create: function () {
                var elem = this.element,
                    opts = this.options;

                $.extend(this, {
                    _classes: ""
                });

                if (!opts.enhanced) {
                    elem.addClass("ui-collapsible-set " +
                        this._themeClassFromOption("ui-group-theme-", opts.theme) + " " +
                        (opts.corners && opts.inset ? "ui-corner-all " : ""));
                    this.element.find($.mobile.collapsible.initSelector).collapsible();
                }

                this._on(elem, { collapsibleexpand: "_handleCollapsibleExpand" });
            },

            _themeClassFromOption: function (prefix, value) {
                return (value ? (value === "none" ? "" : prefix + value) : "");
            },

            _init: function () {
                this._refresh(true);

                // Because the corners are handled by the collapsible itself and the default state is collapsed
                // That was causing https://github.com/jquery/jquery-mobile/issues/4116
                this.element
                    .children(childCollapsiblesSelector)
                    .filter(":jqmData(collapsed='false')")
                    .collapsible("expand");
            },

            _setOptions: function (options) {
                var ret, hasCorners,
                    elem = this.element,
                    themeClass = this._themeClassFromOption("ui-group-theme-", options.theme);

                if (themeClass) {
                    elem
                        .removeClass(this._themeClassFromOption("ui-group-theme-", this.options.theme))
                        .addClass(themeClass);
                }

                if (options.inset !== undefined) {
                    hasCorners = !!(options.inset && (options.corners || this.options.corners));
                }

                if (options.corners !== undefined) {
                    hasCorners = !!(options.corners && (options.inset || this.options.inset));
                }

                if (hasCorners !== undefined) {
                    elem.toggleClass("ui-corner-all", hasCorners);
                }

                ret = this._super(options);
                this.element.children(":mobile-collapsible").collapsible("refresh");
                return ret;
            },

            _destroy: function () {
                var el = this.element;

                this._removeFirstLastClasses(el.children(childCollapsiblesSelector));
                el
                    .removeClass("ui-collapsible-set ui-corner-all " +
                        this._themeClassFromOption("ui-group-theme-", this.options.theme))
                    .children(":mobile-collapsible")
                    .collapsible("destroy");
            },

            _refresh: function (create) {
                var collapsiblesInSet = this.element.children(childCollapsiblesSelector);

                this.element.find($.mobile.collapsible.initSelector).not(".ui-collapsible").collapsible();

                this._addFirstLastClasses(collapsiblesInSet, this._getVisibles(collapsiblesInSet, create), create);
            },

            refresh: function () {
                this._refresh(false);
            }
        }, $.mobile.behaviors.addFirstLastClasses));

    })(jQuery);

    (function ($, undefined) {

        $.widget("mobile.controlgroup", $.extend({
            options: {
                enhanced: false,
                theme: null,
                shadow: false,
                corners: true,
                excludeInvisible: true,
                type: "vertical",
                mini: false
            },

            _create: function () {
                var elem = this.element,
                    opts = this.options,
                    keepNative = $.mobile.page.prototype.keepNativeSelector();

                // Run buttonmarkup
                if ($.fn.buttonMarkup) {
                    this.element
                        .find($.fn.buttonMarkup.initSelector)
                        .not(keepNative)
                        .buttonMarkup();
                }
                // Enhance child widgets
                $.each(this._childWidgets, $.proxy(function (number, widgetName) {
                    if ($.mobile[widgetName]) {
                        this.element
                            .find($.mobile[widgetName].initSelector)
                            .not(keepNative)[widgetName]();
                    }
                }, this));

                $.extend(this, {
                    _ui: null,
                    _initialRefresh: true
                });

                if (opts.enhanced) {
                    this._ui = {
                        groupLegend: elem.children(".ui-controlgroup-label").children(),
                        childWrapper: elem.children(".ui-controlgroup-controls")
                    };
                } else {
                    this._ui = this._enhance();
                }

            },

            _childWidgets: ["checkboxradio", "selectmenu", "button"],

            _themeClassFromOption: function (value) {
                return (value ? (value === "none" ? "" : "ui-group-theme-" + value) : "");
            },

            _enhance: function () {
                var elem = this.element,
                    opts = this.options,
                    ui = {
                        groupLegend: elem.children("legend"),
                        childWrapper: elem
                            .addClass("ui-controlgroup " +
                                "ui-controlgroup-" +
                                    (opts.type === "horizontal" ? "horizontal" : "vertical") + " " +
                                this._themeClassFromOption(opts.theme) + " " +
                                (opts.corners ? "ui-corner-all " : "") +
                                (opts.mini ? "ui-mini " : ""))
                            .wrapInner("<div " +
                                "class='ui-controlgroup-controls " +
                                    (opts.shadow === true ? "ui-shadow" : "") + "'></div>")
                            .children()
                    };

                if (ui.groupLegend.length > 0) {
                    $("<div role='heading' class='ui-controlgroup-label'></div>")
                        .append(ui.groupLegend)
                        .prependTo(elem);
                }

                return ui;
            },

            _init: function () {
                this.refresh();
            },

            _setOptions: function (options) {
                var callRefresh, returnValue,
                    elem = this.element;

                // Must have one of horizontal or vertical
                if (options.type !== undefined) {
                    elem
                        .removeClass("ui-controlgroup-horizontal ui-controlgroup-vertical")
                        .addClass("ui-controlgroup-" + (options.type === "horizontal" ? "horizontal" : "vertical"));
                    callRefresh = true;
                }

                if (options.theme !== undefined) {
                    elem
                        .removeClass(this._themeClassFromOption(this.options.theme))
                        .addClass(this._themeClassFromOption(options.theme));
                }

                if (options.corners !== undefined) {
                    elem.toggleClass("ui-corner-all", options.corners);
                }

                if (options.mini !== undefined) {
                    elem.toggleClass("ui-mini", options.mini);
                }

                if (options.shadow !== undefined) {
                    this._ui.childWrapper.toggleClass("ui-shadow", options.shadow);
                }

                if (options.excludeInvisible !== undefined) {
                    this.options.excludeInvisible = options.excludeInvisible;
                    callRefresh = true;
                }

                returnValue = this._super(options);

                if (callRefresh) {
                    this.refresh();
                }

                return returnValue;
            },

            container: function () {
                return this._ui.childWrapper;
            },

            refresh: function () {
                var $el = this.container(),
                    els = $el.find(".ui-btn").not(".ui-slider-handle"),
                    create = this._initialRefresh;
                if ($.mobile.checkboxradio) {
                    $el.find(":mobile-checkboxradio").checkboxradio("refresh");
                }
                this._addFirstLastClasses(els,
                    this.options.excludeInvisible ? this._getVisibles(els, create) : els,
                    create);
                this._initialRefresh = false;
            },

            // Caveat: If the legend is not the first child of the controlgroup at enhance
            // time, it will be after _destroy().
            _destroy: function () {
                var ui, buttons,
                    opts = this.options;

                if (opts.enhanced) {
                    return this;
                }

                ui = this._ui;
                buttons = this.element
                    .removeClass("ui-controlgroup " +
                        "ui-controlgroup-horizontal ui-controlgroup-vertical ui-corner-all ui-mini " +
                        this._themeClassFromOption(opts.theme))
                    .find(".ui-btn")
                    .not(".ui-slider-handle");

                this._removeFirstLastClasses(buttons);

                ui.groupLegend.unwrap();
                ui.childWrapper.children().unwrap();
            }
        }, $.mobile.behaviors.addFirstLastClasses));

    })(jQuery);

    (function ($, window, undefined) {

        $.widget("mobile.dialog", {
            options: {

                // Accepts left, right and none
                closeBtn: "left",
                closeBtnText: "Close",
                overlayTheme: "a",
                corners: true
            },

            // Override the theme set by the page plugin on pageshow
            _handlePageBeforeShow: function () {
                this._isCloseable = true;
                if (this.options.overlayTheme) {
                    this.element
                        .page("removeContainerBackground")
                        .page("setContainerBackground", this.options.overlayTheme);
                }
            },

            _handlePageBeforeHide: function () {
                this._isCloseable = false;
            },

            // click and submit events:
            // - clicks and submits should use the closing transition that the dialog
            //   opened with unless a data-transition is specified on the link/form
            // - if the click was on the close button, or the link has a data-rel="back"
            //   it'll go back in history naturally
            _handleVClickSubmit: function (event) {
                var attrs,
                    $target = $(event.target).closest(event.type === "vclick" ? "a" : "form");

                if ($target.length && !$target.jqmData("transition")) {
                    attrs = {};
                    attrs["data-" + $.mobile.ns + "transition"] =
                        ($.mobile.navigate.history.getActive() || {})["transition"] ||
                        $.mobile.defaultDialogTransition;
                    attrs["data-" + $.mobile.ns + "direction"] = "reverse";
                    $target.attr(attrs);
                }
            },

            _create: function () {
                var elem = this.element,
                    opts = this.options;

                // Class the markup for dialog styling and wrap interior
                elem.addClass("ui-dialog")
                    .wrapInner($("<div/>", {

                        // ARIA role
                        "role": "dialog",
                        "class": "ui-dialog-contain ui-overlay-shadow" +
                            (!!opts.corners ? " ui-corner-all" : "")
                    }));

                $.extend(this, {
                    _isCloseable: false,
                    _inner: elem.children(),
                    _headerCloseButton: null
                });

                this._on(elem, {
                    vclick: "_handleVClickSubmit",
                    submit: "_handleVClickSubmit",
                    pagebeforeshow: "_handlePageBeforeShow",
                    pagebeforehide: "_handlePageBeforeHide"
                });

                this._setCloseBtn(opts.closeBtn);
            },

            _setOptions: function (options) {
                var closeButtonLocation, closeButtonText,
                    currentOpts = this.options;

                if (options.corners !== undefined) {
                    this._inner.toggleClass("ui-corner-all", !!options.corners);
                }

                if (options.overlayTheme !== undefined) {
                    if ($.mobile.activePage[0] === this.element[0]) {
                        currentOpts.overlayTheme = options.overlayTheme;
                        this._handlePageBeforeShow();
                    }
                }

                if (options.closeBtnText !== undefined) {
                    closeButtonLocation = currentOpts.closeBtn;
                    closeButtonText = options.closeBtnText;
                }

                if (options.closeBtn !== undefined) {
                    closeButtonLocation = options.closeBtn;
                }

                if (closeButtonLocation) {
                    this._setCloseBtn(closeButtonLocation, closeButtonText);
                }

                this._super(options);
            },

            _setCloseBtn: function (location, text) {
                var dst,
                    btn = this._headerCloseButton;

                // Sanitize value
                location = "left" === location ? "left" : "right" === location ? "right" : "none";

                if ("none" === location) {
                    if (btn) {
                        btn.remove();
                        btn = null;
                    }
                } else if (btn) {
                    btn.removeClass("ui-btn-left ui-btn-right").addClass("ui-btn-" + location);
                    if (text) {
                        btn.text(text);
                    }
                } else {
                    dst = this._inner.find(":jqmData(role='header')").first();
                    btn = $("<a></a>", {
                        "role": "button",
                        "href": "#",
                        "class": "ui-btn ui-corner-all ui-icon-delete ui-btn-icon-notext ui-btn-" + location
                    })
                        .text(text || this.options.closeBtnText || "")
                        .prependTo(dst);
                    this._on(btn, { click: "close" });
                }

                this._headerCloseButton = btn;
            },

            // Close method goes back in history
            close: function () {
                var hist = $.mobile.navigate.history;

                if (this._isCloseable) {
                    this._isCloseable = false;
                    // If the hash listening is enabled and there is at least one preceding history
                    // entry it's ok to go back. Initial pages with the dialog hash state are an example
                    // where the stack check is necessary
                    if ($.mobile.hashListeningEnabled && hist.activeIndex > 0) {
                        $.mobile.back();
                    } else {
                        $.mobile.pageContainer.pagecontainer("back");
                    }
                }
            }
        });

    })(jQuery, this);

    (function ($, undefined) {

        // TODO rename filterCallback/deprecate and default to the item itself as the first argument
        var defaultFilterCallback = function (index, searchValue) {
            return (("" + ($.mobile.getAttribute(this, "filtertext") || $(this).text()))
                .toLowerCase().indexOf(searchValue) === -1);
        };

        $.widget("mobile.filterable", {

            initSelector: ":jqmData(filter='true')",

            options: {
                filterReveal: false,
                filterCallback: defaultFilterCallback,
                enhanced: false,
                input: null,
                children: "> li, > option, > optgroup option, > tbody tr, > .ui-controlgroup-controls > .ui-btn, > .ui-controlgroup-controls > .ui-checkbox, > .ui-controlgroup-controls > .ui-radio"
            },

            _create: function () {
                var opts = this.options;

                $.extend(this, {
                    _search: null,
                    _timer: 0
                });

                this._setInput(opts.input);
                if (!opts.enhanced) {
                    this._filterItems(((this._search && this._search.val()) || "").toLowerCase());
                }
            },

            _onKeyUp: function () {
                var val, lastval,
                    search = this._search;

                if (search) {
                    val = search.val().toLowerCase(),
                    lastval = $.mobile.getAttribute(search[0], "lastval") + "";

                    if (lastval && lastval === val) {
                        // Execute the handler only once per value change
                        return;
                    }

                    if (this._timer) {
                        window.clearTimeout(this._timer);
                        this._timer = 0;
                    }

                    this._timer = this._delay(function () {
                        if (this._trigger("beforefilter", null, { input: search }) === false) {
                            return false;
                        }

                        // Change val as lastval for next execution
                        search[0].setAttribute("data-" + $.mobile.ns + "lastval", val);

                        this._filterItems(val);
                        this._timer = 0;
                    }, 250);
                }
            },

            _getFilterableItems: function () {
                var elem = this.element,
                    children = this.options.children,
                    items = !children ? { length: 0 } :
                        $.isFunction(children) ? children() :
                        children.nodeName ? $(children) :
                        children.jquery ? children :
                        this.element.find(children);

                if (items.length === 0) {
                    items = elem.children();
                }

                return items;
            },

            _filterItems: function (val) {
                var idx, callback, length, dst,
                    show = [],
                    hide = [],
                    opts = this.options,
                    filterItems = this._getFilterableItems();

                if (val != null) {
                    callback = opts.filterCallback || defaultFilterCallback;
                    length = filterItems.length;

                    // Partition the items into those to be hidden and those to be shown
                    for (idx = 0 ; idx < length ; idx++) {
                        dst = (callback.call(filterItems[idx], idx, val)) ? hide : show;
                        dst.push(filterItems[idx]);
                    }
                }

                // If nothing is hidden, then the decision whether to hide or show the items
                // is based on the "filterReveal" option.
                if (hide.length === 0) {
                    filterItems[(opts.filterReveal && val.length === 0) ?
                        "addClass" : "removeClass"]("ui-screen-hidden");
                } else {
                    $(hide).addClass("ui-screen-hidden");
                    $(show).removeClass("ui-screen-hidden");
                }

                this._refreshChildWidget();

                this._trigger("filter", null, {
                    items: filterItems
                });
            },

            // The Default implementation of _refreshChildWidget attempts to call
            // refresh on collapsibleset, controlgroup, selectmenu, or listview
            _refreshChildWidget: function () {
                var widget, idx,
                    recognizedWidgets = ["collapsibleset", "selectmenu", "controlgroup", "listview"];

                for (idx = recognizedWidgets.length - 1 ; idx > -1 ; idx--) {
                    widget = recognizedWidgets[idx];
                    if ($.mobile[widget]) {
                        widget = this.element.data("mobile-" + widget);
                        if (widget && $.isFunction(widget.refresh)) {
                            widget.refresh();
                        }
                    }
                }
            },

            // TODO: When the input is not internal, do not even store it in this._search
            _setInput: function (selector) {
                var search = this._search;

                // Stop a pending filter operation
                if (this._timer) {
                    window.clearTimeout(this._timer);
                    this._timer = 0;
                }

                if (search) {
                    this._off(search, "keyup change input");
                    search = null;
                }

                if (selector) {
                    search = selector.jquery ? selector :
                        selector.nodeName ? $(selector) :
                        this.document.find(selector);

                    this._on(search, {
                        keydown: "_onKeyDown",
                        keypress: "_onKeyPress",
                        keyup: "_onKeyUp",
                        change: "_onKeyUp",
                        input: "_onKeyUp"
                    });
                }

                this._search = search;
            },

            // Prevent form submission
            _onKeyDown: function (event) {
                if (event.keyCode === $.ui.keyCode.ENTER) {
                    event.preventDefault();
                    this._preventKeyPress = true;
                }
            },

            _onKeyPress: function (event) {
                if (this._preventKeyPress) {
                    event.preventDefault();
                    this._preventKeyPress = false;
                }
            },

            _setOptions: function (options) {
                var refilter = !((options.filterReveal === undefined) &&
                        (options.filterCallback === undefined) &&
                        (options.children === undefined));

                this._super(options);

                if (options.input !== undefined) {
                    this._setInput(options.input);
                    refilter = true;
                }

                if (refilter) {
                    this.refresh();
                }
            },

            _destroy: function () {
                var opts = this.options,
                    items = this._getFilterableItems();

                if (opts.enhanced) {
                    items.toggleClass("ui-screen-hidden", opts.filterReveal);
                } else {
                    items.removeClass("ui-screen-hidden");
                }
            },

            refresh: function () {
                if (this._timer) {
                    window.clearTimeout(this._timer);
                    this._timer = 0;
                }
                this._filterItems(((this._search && this._search.val()) || "").toLowerCase());
            }
        });

    })(jQuery);

    (function ($, undefined) {

        var getAttr = $.mobile.getAttribute;

        $.widget("mobile.listview", $.extend({

            options: {
                theme: null,
                countTheme: null, /* Deprecated in 1.4 */
                dividerTheme: null,
                icon: "carat-r",
                splitIcon: "carat-r",
                splitTheme: null,
                corners: true,
                shadow: true,
                inset: false
            },

            _create: function () {
                var t = this,
                    listviewClasses = "";

                listviewClasses += t.options.inset ? " ui-listview-inset" : "";

                if (!!t.options.inset) {
                    listviewClasses += t.options.corners ? " ui-corner-all" : "";
                    listviewClasses += t.options.shadow ? " ui-shadow" : "";
                }

                // create listview markup
                t.element.addClass(" ui-listview" + listviewClasses);

                t.refresh(true);
            },

            // TODO: Remove in 1.5
            _findFirstElementByTagName: function (ele, nextProp, lcName, ucName) {
                var dict = {};
                dict[lcName] = dict[ucName] = true;
                while (ele) {
                    if (dict[ele.nodeName]) {
                        return ele;
                    }
                    ele = ele[nextProp];
                }
                return null;
            },
            // TODO: Remove in 1.5
            _addThumbClasses: function (containers) {
                var i, img, len = containers.length;
                for (i = 0; i < len; i++) {
                    img = $(this._findFirstElementByTagName(containers[i].firstChild, "nextSibling", "img", "IMG"));
                    if (img.length) {
                        $(this._findFirstElementByTagName(img[0].parentNode, "parentNode", "li", "LI")).addClass(img.hasClass("ui-li-icon") ? "ui-li-has-icon" : "ui-li-has-thumb");
                    }
                }
            },

            _getChildrenByTagName: function (ele, lcName, ucName) {
                var results = [],
                    dict = {};
                dict[lcName] = dict[ucName] = true;
                ele = ele.firstChild;
                while (ele) {
                    if (dict[ele.nodeName]) {
                        results.push(ele);
                    }
                    ele = ele.nextSibling;
                }
                return $(results);
            },

            _beforeListviewRefresh: $.noop,
            _afterListviewRefresh: $.noop,

            refresh: function (create) {
                var buttonClass, pos, numli, item, itemClass, itemTheme, itemIcon, icon, a,
                    isDivider, startCount, newStartCount, value, last, splittheme, splitThemeClass, spliticon,
                    altButtonClass, dividerTheme, li,
                    o = this.options,
                    $list = this.element,
                    ol = !!$.nodeName($list[0], "ol"),
                    start = $list.attr("start"),
                    itemClassDict = {},
                    countBubbles = $list.find(".ui-li-count"),
                    countTheme = getAttr($list[0], "counttheme") || this.options.countTheme,
                    countThemeClass = countTheme ? "ui-body-" + countTheme : "ui-body-inherit";

                if (o.theme) {
                    $list.addClass("ui-group-theme-" + o.theme);
                }

                // Check if a start attribute has been set while taking a value of 0 into account
                if (ol && (start || start === 0)) {
                    startCount = parseInt(start, 10) - 1;
                    $list.css("counter-reset", "listnumbering " + startCount);
                }

                this._beforeListviewRefresh();

                li = this._getChildrenByTagName($list[0], "li", "LI");

                for (pos = 0, numli = li.length; pos < numli; pos++) {
                    item = li.eq(pos);
                    itemClass = "";

                    if (create || item[0].className.search(/\bui-li-static\b|\bui-li-divider\b/) < 0) {
                        a = this._getChildrenByTagName(item[0], "a", "A");
                        isDivider = (getAttr(item[0], "role") === "list-divider");
                        value = item.attr("value");
                        itemTheme = getAttr(item[0], "theme");

                        if (a.length && a[0].className.search(/\bui-btn\b/) < 0 && !isDivider) {
                            itemIcon = getAttr(item[0], "icon");
                            icon = (itemIcon === false) ? false : (itemIcon || o.icon);

                            // TODO: Remove in 1.5 together with links.js (links.js / .ui-link deprecated in 1.4)
                            a.removeClass("ui-link");

                            buttonClass = "ui-btn";

                            if (itemTheme) {
                                buttonClass += " ui-btn-" + itemTheme;
                            }

                            if (a.length > 1) {
                                itemClass = "ui-li-has-alt";

                                last = a.last();
                                splittheme = getAttr(last[0], "theme") || o.splitTheme || getAttr(item[0], "theme", true);
                                splitThemeClass = splittheme ? " ui-btn-" + splittheme : "";
                                spliticon = getAttr(last[0], "icon") || getAttr(item[0], "icon") || o.splitIcon;
                                altButtonClass = "ui-btn ui-btn-icon-notext ui-icon-" + spliticon + splitThemeClass;

                                last
                                    .attr("title", $.trim(last.getEncodedText()))
                                    .addClass(altButtonClass)
                                    .empty();

                                // Reduce to the first anchor, because only the first gets the buttonClass
                                a = a.first();
                            } else if (icon) {
                                buttonClass += " ui-btn-icon-right ui-icon-" + icon;
                            }

                            // Apply buttonClass to the (first) anchor
                            a.addClass(buttonClass);
                        } else if (isDivider) {
                            dividerTheme = (getAttr(item[0], "theme") || o.dividerTheme || o.theme);

                            itemClass = "ui-li-divider ui-bar-" + (dividerTheme ? dividerTheme : "inherit");

                            item.attr("role", "heading");
                        } else if (a.length <= 0) {
                            itemClass = "ui-li-static ui-body-" + (itemTheme ? itemTheme : "inherit");
                        }
                        if (ol && value) {
                            newStartCount = parseInt(value, 10) - 1;

                            item.css("counter-reset", "listnumbering " + newStartCount);
                        }
                    }

                    // Instead of setting item class directly on the list item
                    // at this point in time, push the item into a dictionary
                    // that tells us what class to set on it so we can do this after this
                    // processing loop is finished.

                    if (!itemClassDict[itemClass]) {
                        itemClassDict[itemClass] = [];
                    }

                    itemClassDict[itemClass].push(item[0]);
                }

                // Set the appropriate listview item classes on each list item.
                // The main reason we didn't do this
                // in the for-loop above is because we can eliminate per-item function overhead
                // by calling addClass() and children() once or twice afterwards. This
                // can give us a significant boost on platforms like WP7.5.

                for (itemClass in itemClassDict) {
                    $(itemClassDict[itemClass]).addClass(itemClass);
                }

                countBubbles.each(function () {
                    $(this).closest("li").addClass("ui-li-has-count");
                });
                if (countThemeClass) {
                    countBubbles.not("[class*='ui-body-']").addClass(countThemeClass);
                }

                // Deprecated in 1.4. From 1.5 you have to add class ui-li-has-thumb or ui-li-has-icon to the LI.
                this._addThumbClasses(li);
                this._addThumbClasses(li.find(".ui-btn"));

                this._afterListviewRefresh();

                this._addFirstLastClasses(li, this._getVisibles(li, create), create);
            }
        }, $.mobile.behaviors.addFirstLastClasses));

    })(jQuery);

    (function ($, undefined) {

        var rdivider = /(^|\s)ui-li-divider($|\s)/,
            rhidden = /(^|\s)ui-screen-hidden($|\s)/;

        $.widget("mobile.listview", $.mobile.listview, {
            options: {
                hideDividers: false
            },

            _afterListviewRefresh: function () {
                var items, idx, item, hideDivider = true;

                this._superApply(arguments);

                if (this.options.hideDividers) {
                    items = this._getChildrenByTagName(this.element[0], "li", "LI");
                    for (idx = items.length - 1 ; idx > -1 ; idx--) {
                        item = items[idx];
                        if (item.className.match(rdivider)) {
                            if (hideDivider) {
                                item.className = item.className + " ui-screen-hidden";
                            }
                            hideDivider = true;
                        } else {
                            if (!item.className.match(rhidden)) {
                                hideDivider = false;
                            }
                        }
                    }
                }
            }
        });

    })(jQuery);

    (function ($, undefined) {

        // Create a function that will replace the _setOptions function of a widget,
        // and will pass the options on to the input of the filterable.
        var replaceSetOptions = function (self, orig) {
            return function (options) {
                orig.call(this, options);
                self._syncTextInputOptions(options);
            };
        },
            rDividerListItem = /(^|\s)ui-li-divider(\s|$)/,
            origDefaultFilterCallback = $.mobile.filterable.prototype.options.filterCallback;

        // Override the default filter callback with one that does not hide list dividers
        $.mobile.filterable.prototype.options.filterCallback = function (index, searchValue) {
            return !this.className.match(rDividerListItem) &&
                origDefaultFilterCallback.call(this, index, searchValue);
        };

        $.widget("mobile.filterable", $.mobile.filterable, {
            options: {
                filterPlaceholder: "Filter items...",
                filterTheme: null
            },

            _create: function () {
                var idx, widgetName,
                    elem = this.element,
                    recognizedWidgets = ["collapsibleset", "selectmenu", "controlgroup", "listview"],
                    createHandlers = {};

                this._super();

                $.extend(this, {
                    _widget: null
                });

                for (idx = recognizedWidgets.length - 1 ; idx > -1 ; idx--) {
                    widgetName = recognizedWidgets[idx];
                    if ($.mobile[widgetName]) {
                        if (this._setWidget(elem.data("mobile-" + widgetName))) {
                            break;
                        } else {
                            createHandlers[widgetName + "create"] = "_handleCreate";
                        }
                    }
                }

                if (!this._widget) {
                    this._on(elem, createHandlers);
                }
            },

            _handleCreate: function (evt) {
                this._setWidget(this.element.data("mobile-" + evt.type.substring(0, evt.type.length - 6)));
            },

            _trigger: function (type, event, data) {
                if (this._widget && this._widget.widgetFullName === "mobile-listview" &&
                    type === "beforefilter") {

                    // Also trigger listviewbeforefilter if this widget is also a listview
                    this._widget._trigger("beforefilter", event, data);
                }

                // Passing back the response enables calling preventDefault()
                return this._super(type, event, data);
            },

            _setWidget: function (widget) {
                if (!this._widget && widget) {
                    this._widget = widget;
                    this._widget._setOptions = replaceSetOptions(this, this._widget._setOptions);
                }

                if (!!this._widget) {
                    this._syncTextInputOptions(this._widget.options);
                    if (this._widget.widgetName === "listview") {
                        this._widget.options.hideDividers = true;
                        this._widget.element.listview("refresh");
                    }
                }

                return !!this._widget;
            },

            _isSearchInternal: function () {
                return (this._search && this._search.jqmData("ui-filterable-" + this.uuid + "-internal"));
            },

            _setInput: function (selector) {
                var opts = this.options,
                    updatePlaceholder = true,
                    textinputOpts = {};

                if (!selector) {
                    if (this._isSearchInternal()) {

                        // Ignore the call to set a new input if the selector goes to falsy and
                        // the current textinput is already of the internally generated variety.
                        return;
                    } else {

                        // Generating a new textinput widget. No need to set the placeholder
                        // further down the function.
                        updatePlaceholder = false;
                        selector = $("<input " +
                            "data-" + $.mobile.ns + "type='search' " +
                            "placeholder='" + opts.filterPlaceholder + "'></input>")
                            .jqmData("ui-filterable-" + this.uuid + "-internal", true);
                        $("<form class='ui-filterable'></form>")
                            .append(selector)
                            .submit(function (evt) {
                                evt.preventDefault();
                                selector.blur();
                            })
                            .insertBefore(this.element);
                        if ($.mobile.textinput) {
                            if (this.options.filterTheme != null) {
                                textinputOpts["theme"] = opts.filterTheme;
                            }

                            selector.textinput(textinputOpts);
                        }
                    }
                }

                this._super(selector);

                if (this._isSearchInternal() && updatePlaceholder) {
                    this._search.attr("placeholder", this.options.filterPlaceholder);
                }
            },

            _setOptions: function (options) {
                var ret = this._super(options);

                // Need to set the filterPlaceholder after having established the search input
                if (options.filterPlaceholder !== undefined) {
                    if (this._isSearchInternal()) {
                        this._search.attr("placeholder", options.filterPlaceholder);
                    }
                }

                if (options.filterTheme !== undefined && this._search && $.mobile.textinput) {
                    this._search.textinput("option", "theme", options.filterTheme);
                }

                return ret;
            },

            // The listview implementation accompanying this filterable backcompat layer will call
            // filterable.refresh() after it's done refreshing the listview to make sure the filterable
            // filters out any new items added. However, when the listview refresh has been initiated by
            // the filterable itself, then such filtering has already taken place, and calling the
            // filterable's refresh() method will cause an infinite recursion. We stop this by setting a
            // flag that will cause the filterable's refresh() method to short-circuit.
            _refreshChildWidget: function () {
                this._refreshingChildWidget = true;
                this._superApply(arguments);
                this._refreshingChildWidget = false;
            },

            refresh: function () {
                if (!this._refreshingChildWidget) {
                    this._superApply(arguments);
                }
            },

            _destroy: function () {
                if (this._isSearchInternal()) {
                    this._search.remove();
                }
                this._super();
            },

            _syncTextInputOptions: function (options) {
                var idx,
                    textinputOptions = {};

                // We only sync options if the filterable's textinput is of the internally
                // generated variety, rather than one specified by the user.
                if (this._isSearchInternal() && $.mobile.textinput) {

                    // Apply only the options understood by textinput
                    for (idx in $.mobile.textinput.prototype.options) {
                        if (options[idx] !== undefined) {
                            if (idx === "theme" && this.options.filterTheme != null) {
                                textinputOptions[idx] = this.options.filterTheme;
                            } else {
                                textinputOptions[idx] = options[idx];
                            }
                        }
                    }
                    this._search.textinput("option", textinputOptions);
                }
            }
        });

        // Instantiate a filterable on a listview that has the data-filter="true" attribute
        // This is not necessary for static content, because the auto-enhance takes care of instantiating
        // the filterable upon encountering data-filter="true". However, because of 1.3.x it is expected
        // that a listview with data-filter="true" will be filterable even if you just instantiate a
        // listview on it. The extension below ensures that this continues to happen in 1.4.x.
        $.widget("mobile.listview", $.mobile.listview, {
            options: {
                filter: false
            },
            _create: function () {
                if (this.options.filter === true &&
                        !this.element.data("mobile-filterable")) {
                    this.element.filterable();
                }
                return this._super();
            },

            refresh: function () {
                var filterable;

                this._superApply(arguments);

                if (this.options.filter === true) {
                    filterable = this.element.data("mobile-filterable");

                    if (filterable) {
                        filterable.refresh();
                    }
                }
            }
        });

    })(jQuery);

    (function ($, undefined) {

        $.widget("mobile.toolbar", {
            initSelector: ":jqmData(role='footer'), :jqmData(role='header')",

            options: {
                theme: null,
                addBackBtn: false,
                backBtnTheme: null,
                backBtnText: "Back"
            },

            _create: function () {
                var leftbtn, rightbtn,
                    role = this.element.is(":jqmData(role='header')") ? "header" : "footer",
                    page = this.element.closest(".ui-page");
                if (page.length === 0) {
                    page = false;
                    this._on(this.document, {
                        "pageshow": "refresh"
                    });
                }
                $.extend(this, {
                    role: role,
                    page: page,
                    leftbtn: leftbtn,
                    rightbtn: rightbtn
                });
                this.element.attr("role", role === "header" ? "banner" : "contentinfo").addClass("ui-" + role);
                this.refresh();
                this._setOptions(this.options);
            },
            _setOptions: function (o) {
                if (o.addBackBtn !== undefined) {
                    this._updateBackButton();
                }
                if (o.backBtnTheme != null) {
                    this.element
                        .find(".ui-toolbar-back-btn")
                        .addClass("ui-btn ui-btn-" + o.backBtnTheme);
                }
                if (o.backBtnText !== undefined) {
                    this.element.find(".ui-toolbar-back-btn .ui-btn-text").text(o.backBtnText);
                }
                if (o.theme !== undefined) {
                    var currentTheme = this.options.theme ? this.options.theme : "inherit",
                        newTheme = o.theme ? o.theme : "inherit";

                    this.element.removeClass("ui-bar-" + currentTheme).addClass("ui-bar-" + newTheme);
                }

                this._super(o);
            },
            refresh: function () {
                if (this.role === "header") {
                    this._addHeaderButtonClasses();
                }
                if (!this.page) {
                    this._setRelative();
                    if (this.role === "footer") {
                        this.element.appendTo("body");
                    } else if (this.role === "header") {
                        this._updateBackButton();
                    }
                }
                this._addHeadingClasses();
                this._btnMarkup();
            },

            //we only want this to run on non fixed toolbars so make it easy to override
            _setRelative: function () {
                $("[data-" + $.mobile.ns + "role='page']").css({ "position": "relative" });
            },

            // Deprecated in 1.4. As from 1.5 button classes have to be present in the markup.
            _btnMarkup: function () {
                this.element
                    .children("a")
                    .filter(":not([data-" + $.mobile.ns + "role='none'])")
                    .attr("data-" + $.mobile.ns + "role", "button");
                this.element.trigger("create");
            },
            // Deprecated in 1.4. As from 1.5 ui-btn-left/right classes have to be present in the markup.
            _addHeaderButtonClasses: function () {
                var headerAnchors = this.element.children("a, button");

                // Do not mistake a back button for a left toolbar button
                this.leftbtn = headerAnchors.hasClass("ui-btn-left") &&
                    !headerAnchors.hasClass("ui-toolbar-back-btn");

                this.rightbtn = headerAnchors.hasClass("ui-btn-right");

                // Filter out right buttons and back buttons
                this.leftbtn = this.leftbtn ||
                    headerAnchors.eq(0)
                        .not(".ui-btn-right,.ui-toolbar-back-btn")
                        .addClass("ui-btn-left")
                        .length;

                this.rightbtn = this.rightbtn || headerAnchors.eq(1).addClass("ui-btn-right").length;
            },
            _updateBackButton: function () {
                var backButton,
                    options = this.options,
                    theme = options.backBtnTheme || options.theme;

                // Retrieve the back button or create a new, empty one
                backButton = this._backButton = (this._backButton || {});

                // We add a back button only if the option to do so is on
                if (this.options.addBackBtn &&

                    // This must also be a header toolbar
                        this.role === "header" &&

                    // There must be multiple pages in the DOM
                        $(".ui-page").length > 1 &&
                        (this.page ?

                    // If the toolbar is internal the page's URL must differ from the hash
                            (this.page[0].getAttribute("data-" + $.mobile.ns + "url") !==
                                $.mobile.path.stripHash(location.hash)) :

                    // Otherwise, if the toolbar is external there must be at least one
                    // history item to which one can go back
                            ($.mobile.navigate && $.mobile.navigate.history &&
                                $.mobile.navigate.history.activeIndex > 0)) &&

                    // The toolbar does not have a left button
                        !this.leftbtn) {

                    // Skip back button creation if one is already present
                    if (!backButton.attached) {
                        this.backButton = backButton.element = (backButton.element ||
                            $("<a role='button' href='javascript:void(0);' " +
                                "class='ui-btn ui-corner-all ui-shadow ui-btn-left " +
                                    (theme ? "ui-btn-" + theme + " " : "") +
                                    "ui-toolbar-back-btn ui-icon-carat-l ui-btn-icon-left' " +
                                "data-" + $.mobile.ns + "rel='back'>" + options.backBtnText +
                                "</a>"))
                                .prependTo(this.element);
                        backButton.attached = true;
                    }

                    // If we are not adding a back button, then remove the one present, if any
                } else if (backButton.element) {
                    backButton.element.detach();
                    backButton.attached = false;
                }
            },
            _addHeadingClasses: function () {
                this.element.children("h1, h2, h3, h4, h5, h6")
                    .addClass("ui-title")
                    // Regardless of h element number in src, it becomes h1 for the enhanced page
                    .attr({
                        "role": "heading",
                        "aria-level": "1"
                    });
            },
            _destroy: function () {
                var currentTheme;

                this.element.children("h1, h2, h3, h4, h5, h6")
                    .removeClass("ui-title")
                    .removeAttr("role")
                    .removeAttr("aria-level");

                if (this.role === "header") {
                    this.element.children("a, button")
                        .removeClass("ui-btn-left ui-btn-right ui-btn ui-shadow ui-corner-all");
                    if (this.backButton) {
                        this.backButton.remove();
                    }
                }

                currentTheme = this.options.theme ? this.options.theme : "inherit";
                this.element.removeClass("ui-bar-" + currentTheme);

                this.element.removeClass("ui-" + this.role).removeAttr("role");
            }
        });

    })(jQuery);

    (function ($, undefined) {

        $.widget("mobile.toolbar", $.mobile.toolbar, {
            options: {
                position: null,
                visibleOnPageShow: true,
                disablePageZoom: true,
                transition: "slide", //can be none, fade, slide (slide maps to slideup or slidedown)
                fullscreen: false,
                tapToggle: true,
                tapToggleBlacklist: "a, button, input, select, textarea, .ui-header-fixed, .ui-footer-fixed, .ui-flipswitch, .ui-popup, .ui-panel, .ui-panel-dismiss-open",
                hideDuringFocus: "input, textarea, select",
                updatePagePadding: true,
                trackPersistentToolbars: true,

                // Browser detection! Weeee, here we go...
                // Unfortunately, position:fixed is costly, not to mention probably impossible, to feature-detect accurately.
                // Some tests exist, but they currently return false results in critical devices and browsers, which could lead to a broken experience.
                // Testing fixed positioning is also pretty obtrusive to page load, requiring injected elements and scrolling the window
                // The following function serves to rule out some popular browsers with known fixed-positioning issues
                // This is a plugin option like any other, so feel free to improve or overwrite it
                supportBlacklist: function () {
                    return !$.support.fixedPosition;
                }
            },

            _create: function () {
                this._super();
                this.pagecontainer = $(":mobile-pagecontainer");
                if (this.options.position === "fixed" && !this.options.supportBlacklist()) {
                    this._makeFixed();
                }
            },

            _makeFixed: function () {
                this.element.addClass("ui-" + this.role + "-fixed");
                this.updatePagePadding();
                this._addTransitionClass();
                this._bindPageEvents();
                this._bindToggleHandlers();
            },

            _setOptions: function (o) {
                if (o.position === "fixed" && this.options.position !== "fixed") {
                    this._makeFixed();
                }
                if (this.options.position === "fixed" && !this.options.supportBlacklist()) {
                    var $page = (!!this.page) ? this.page : ($(".ui-page-active").length > 0) ? $(".ui-page-active") : $(".ui-page").eq(0);

                    if (o.fullscreen !== undefined) {
                        if (o.fullscreen) {
                            this.element.addClass("ui-" + this.role + "-fullscreen");
                            $page.addClass("ui-page-" + this.role + "-fullscreen");
                        }
                            // If not fullscreen, add class to page to set top or bottom padding
                        else {
                            this.element.removeClass("ui-" + this.role + "-fullscreen");
                            $page.removeClass("ui-page-" + this.role + "-fullscreen").addClass("ui-page-" + this.role + "-fixed");
                        }
                    }
                }
                this._super(o);
            },

            _addTransitionClass: function () {
                var tclass = this.options.transition;

                if (tclass && tclass !== "none") {
                    // use appropriate slide for header or footer
                    if (tclass === "slide") {
                        tclass = this.element.hasClass("ui-header") ? "slidedown" : "slideup";
                    }

                    this.element.addClass(tclass);
                }
            },

            _bindPageEvents: function () {
                var page = (!!this.page) ? this.element.closest(".ui-page") : this.document;
                //page event bindings
                // Fixed toolbars require page zoom to be disabled, otherwise usability issues crop up
                // This method is meant to disable zoom while a fixed-positioned toolbar page is visible
                this._on(page, {
                    "pagebeforeshow": "_handlePageBeforeShow",
                    "webkitAnimationStart": "_handleAnimationStart",
                    "animationstart": "_handleAnimationStart",
                    "updatelayout": "_handleAnimationStart",
                    "pageshow": "_handlePageShow",
                    "pagebeforehide": "_handlePageBeforeHide"
                });
            },

            _handlePageBeforeShow: function () {
                var o = this.options;
                if (o.disablePageZoom) {
                    $.mobile.zoom.disable(true);
                }
                if (!o.visibleOnPageShow) {
                    this.hide(true);
                }
            },

            _handleAnimationStart: function () {
                if (this.options.updatePagePadding) {
                    this.updatePagePadding((!!this.page) ? this.page : ".ui-page-active");
                }
            },

            _handlePageShow: function () {
                this.updatePagePadding((!!this.page) ? this.page : ".ui-page-active");
                if (this.options.updatePagePadding) {
                    this._on(this.window, { "throttledresize": "updatePagePadding" });
                }
            },

            _handlePageBeforeHide: function (e, ui) {
                var o = this.options,
                    thisFooter, thisHeader, nextFooter, nextHeader;

                if (o.disablePageZoom) {
                    $.mobile.zoom.enable(true);
                }
                if (o.updatePagePadding) {
                    this._off(this.window, "throttledresize");
                }

                if (o.trackPersistentToolbars) {
                    thisFooter = $(".ui-footer-fixed:jqmData(id)", this.page);
                    thisHeader = $(".ui-header-fixed:jqmData(id)", this.page);
                    nextFooter = thisFooter.length && ui.nextPage && $(".ui-footer-fixed:jqmData(id='" + thisFooter.jqmData("id") + "')", ui.nextPage) || $();
                    nextHeader = thisHeader.length && ui.nextPage && $(".ui-header-fixed:jqmData(id='" + thisHeader.jqmData("id") + "')", ui.nextPage) || $();

                    if (nextFooter.length || nextHeader.length) {

                        nextFooter.add(nextHeader).appendTo($.mobile.pageContainer);

                        ui.nextPage.one("pageshow", function () {
                            nextHeader.prependTo(this);
                            nextFooter.appendTo(this);
                        });
                    }
                }
            },

            _visible: true,

            // This will set the content element's top or bottom padding equal to the toolbar's height
            updatePagePadding: function (tbPage) {
                var $el = this.element,
                    header = (this.role === "header"),
                    pos = parseFloat($el.css(header ? "top" : "bottom"));

                // This behavior only applies to "fixed", not "fullscreen"
                if (this.options.fullscreen) { return; }
                // tbPage argument can be a Page object or an event, if coming from throttled resize.
                tbPage = (tbPage && tbPage.type === undefined && tbPage) || this.page || $el.closest(".ui-page");
                tbPage = (!!this.page) ? this.page : ".ui-page-active";
                $(tbPage).css("padding-" + (header ? "top" : "bottom"), $el.outerHeight() + pos);
            },

            _useTransition: function (notransition) {
                var $win = this.window,
                    $el = this.element,
                    scroll = $win.scrollTop(),
                    elHeight = $el.height(),
                    pHeight = (!!this.page) ? $el.closest(".ui-page").height() : $(".ui-page-active").height(),
                    viewportHeight = $.mobile.getScreenHeight();

                return !notransition &&
                    (this.options.transition && this.options.transition !== "none" &&
                    (
                        (this.role === "header" && !this.options.fullscreen && scroll > elHeight) ||
                        (this.role === "footer" && !this.options.fullscreen && scroll + viewportHeight < pHeight - elHeight)
                    ) || this.options.fullscreen
                    );
            },

            show: function (notransition) {
                var hideClass = "ui-fixed-hidden",
                    $el = this.element;

                if (this._useTransition(notransition)) {
                    $el
                        .removeClass("out " + hideClass)
                        .addClass("in")
                        .animationComplete(function () {
                            $el.removeClass("in");
                        });
                }
                else {
                    $el.removeClass(hideClass);
                }
                this._visible = true;
            },

            hide: function (notransition) {
                var hideClass = "ui-fixed-hidden",
                    $el = this.element,
                    // if it's a slide transition, our new transitions need the reverse class as well to slide outward
                    outclass = "out" + (this.options.transition === "slide" ? " reverse" : "");

                if (this._useTransition(notransition)) {
                    $el
                        .addClass(outclass)
                        .removeClass("in")
                        .animationComplete(function () {
                            $el.addClass(hideClass).removeClass(outclass);
                        });
                }
                else {
                    $el.addClass(hideClass).removeClass(outclass);
                }
                this._visible = false;
            },

            toggle: function () {
                this[this._visible ? "hide" : "show"]();
            },

            _bindToggleHandlers: function () {
                var self = this,
                    o = self.options,
                    delayShow, delayHide,
                    isVisible = true,
                    page = (!!this.page) ? this.page : $(".ui-page");

                // tap toggle
                page
                    .bind("vclick", function (e) {
                        if (o.tapToggle && !$(e.target).closest(o.tapToggleBlacklist).length) {
                            self.toggle();
                        }
                    })
                    .bind("focusin focusout", function (e) {
                        //this hides the toolbars on a keyboard pop to give more screen room and prevent ios bug which
                        //positions fixed toolbars in the middle of the screen on pop if the input is near the top or
                        //bottom of the screen addresses issues #4410 Footer navbar moves up when clicking on a textbox in an Android environment
                        //and issue #4113 Header and footer change their position after keyboard popup - iOS
                        //and issue #4410 Footer navbar moves up when clicking on a textbox in an Android environment
                        if (screen.width < 1025 && $(e.target).is(o.hideDuringFocus) && !$(e.target).closest(".ui-header-fixed, .ui-footer-fixed").length) {
                            //Fix for issue #4724 Moving through form in Mobile Safari with "Next" and "Previous" system
                            //controls causes fixed position, tap-toggle false Header to reveal itself
                            // isVisible instead of self._visible because the focusin and focusout events fire twice at the same time
                            // Also use a delay for hiding the toolbars because on Android native browser focusin is direclty followed
                            // by a focusout when a native selects opens and the other way around when it closes.
                            if (e.type === "focusout" && !isVisible) {
                                isVisible = true;
                                //wait for the stack to unwind and see if we have jumped to another input
                                clearTimeout(delayHide);
                                delayShow = setTimeout(function () {
                                    self.show();
                                }, 0);
                            } else if (e.type === "focusin" && !!isVisible) {
                                //if we have jumped to another input clear the time out to cancel the show.
                                clearTimeout(delayShow);
                                isVisible = false;
                                delayHide = setTimeout(function () {
                                    self.hide();
                                }, 0);
                            }
                        }
                    });
            },

            _setRelative: function () {
                if (this.options.position !== "fixed") {
                    $("[data-" + $.mobile.ns + "role='page']").css({ "position": "relative" });
                }
            },

            _destroy: function () {
                var pageClasses, toolbarClasses, hasFixed, header, hasFullscreen,
                    page = this.pagecontainer.pagecontainer("getActivePage");

                this._super();
                if (this.options.position === "fixed") {
                    hasFixed = $("body>.ui-" + this.role + "-fixed")
                                .add(page.find(".ui-" + this.options.role + "-fixed"))
                                .not(this.element).length > 0;
                    hasFullscreen = $("body>.ui-" + this.role + "-fixed")
                                .add(page.find(".ui-" + this.options.role + "-fullscreen"))
                                .not(this.element).length > 0;
                    toolbarClasses = "ui-header-fixed ui-footer-fixed ui-header-fullscreen in out" +
                        " ui-footer-fullscreen fade slidedown slideup ui-fixed-hidden";
                    this.element.removeClass(toolbarClasses);
                    if (!hasFullscreen) {
                        pageClasses = "ui-page-" + this.role + "-fullscreen";
                    }
                    if (!hasFixed) {
                        header = this.role === "header";
                        pageClasses += " ui-page-" + this.role + "-fixed";
                        page.css("padding-" + (header ? "top" : "bottom"), "");
                    }
                    page.removeClass(pageClasses);
                }
            }

        });
    })(jQuery);

    (function ($, undefined) {
        $.widget("mobile.toolbar", $.mobile.toolbar, {

            _makeFixed: function () {
                this._super();
                this._workarounds();
            },

            //check the browser and version and run needed workarounds
            _workarounds: function () {
                var ua = navigator.userAgent,
                platform = navigator.platform,
                // Rendering engine is Webkit, and capture major version
                wkmatch = ua.match(/AppleWebKit\/([0-9]+)/),
                wkversion = !!wkmatch && wkmatch[1],
                os = null,
                self = this;
                //set the os we are working in if it dosent match one with workarounds return
                if (platform.indexOf("iPhone") > -1 || platform.indexOf("iPad") > -1 || platform.indexOf("iPod") > -1) {
                    os = "ios";
                } else if (ua.indexOf("Android") > -1) {
                    os = "android";
                } else {
                    return;
                }
                //check os version if it dosent match one with workarounds return
                if (os === "ios") {
                    //iOS  workarounds
                    self._bindScrollWorkaround();
                } else if (os === "android" && wkversion && wkversion < 534) {
                    //Android 2.3 run all Android 2.3 workaround
                    self._bindScrollWorkaround();
                    self._bindListThumbWorkaround();
                } else {
                    return;
                }
            },

            //Utility class for checking header and footer positions relative to viewport
            _viewportOffset: function () {
                var $el = this.element,
                    header = $el.hasClass("ui-header"),
                    offset = Math.abs($el.offset().top - this.window.scrollTop());
                if (!header) {
                    offset = Math.round(offset - this.window.height() + $el.outerHeight()) - 60;
                }
                return offset;
            },

            //bind events for _triggerRedraw() function
            _bindScrollWorkaround: function () {
                var self = this;
                //bind to scrollstop and check if the toolbars are correctly positioned
                this._on(this.window, {
                    scrollstop: function () {
                        var viewportOffset = self._viewportOffset();
                        //check if the header is visible and if its in the right place
                        if (viewportOffset > 2 && self._visible) {
                            self._triggerRedraw();
                        }
                    }
                });
            },

            //this addresses issue #4250 Persistent footer instability in v1.1 with long select lists in Android 2.3.3
            //and issue #3748 Android 2.x: Page transitions broken when fixed toolbars used
            //the absolutely positioned thumbnail in a list view causes problems with fixed position buttons above in a nav bar
            //setting the li's to -webkit-transform:translate3d(0,0,0); solves this problem to avoide potential issues in other
            //platforms we scope this with the class ui-android-2x-fix
            _bindListThumbWorkaround: function () {
                this.element.closest(".ui-page").addClass("ui-android-2x-fixed");
            },
            //this addresses issues #4337 Fixed header problem after scrolling content on iOS and Android
            //and device bugs project issue #1 Form elements can lose click hit area in position: fixed containers.
            //this also addresses not on fixed toolbars page in docs
            //adding 1px of padding to the bottom then removing it causes a "redraw"
            //which positions the toolbars correctly (they will always be visually correct)
            _triggerRedraw: function () {
                var paddingBottom = parseFloat($(".ui-page-active").css("padding-bottom"));
                //trigger page redraw to fix incorrectly positioned fixed elements
                $(".ui-page-active").css("padding-bottom", (paddingBottom + 1) + "px");
                //if the padding is reset with out a timeout the reposition will not occure.
                //this is independant of JQM the browser seems to need the time to react.
                setTimeout(function () {
                    $(".ui-page-active").css("padding-bottom", paddingBottom + "px");
                }, 0);
            },

            destroy: function () {
                this._super();
                //Remove the class we added to the page previously in android 2.x
                this.element.closest(".ui-page-active").removeClass("ui-android-2x-fix");
            }
        });

    })(jQuery);

    (function ($, undefined) {

        $.widget("mobile.textinput", {
            initSelector: "input[type='text']," +
                "input[type='search']," +
                ":jqmData(type='search')," +
                "input[type='number']," +
                ":jqmData(type='number')," +
                "input[type='password']," +
                "input[type='email']," +
                "input[type='url']," +
                "input[type='tel']," +
                "textarea," +
                "input[type='time']," +
                "input[type='date']," +
                "input[type='month']," +
                "input[type='week']," +
                "input[type='datetime']," +
                "input[type='datetime-local']," +
                "input[type='color']," +
                "input:not([type])," +
                "input[type='file']",

            options: {
                theme: null,
                corners: true,
                mini: false,
                // This option defaults to true on iOS devices.
                preventFocusZoom: /iPhone|iPad|iPod/.test(navigator.platform) && navigator.userAgent.indexOf("AppleWebKit") > -1,
                wrapperClass: "",
                enhanced: false
            },

            _create: function () {

                var options = this.options,
                    isSearch = this.element.is("[type='search'], :jqmData(type='search')"),
                    isTextarea = this.element[0].tagName === "TEXTAREA",
                    isRange = this.element.is("[data-" + ($.mobile.ns || "") + "type='range']"),
                    inputNeedsWrap = ((this.element.is("input") ||
                        this.element.is("[data-" + ($.mobile.ns || "") + "type='search']")) &&
                            !isRange);

                if (this.element.prop("disabled")) {
                    options.disabled = true;
                }

                $.extend(this, {
                    classes: this._classesFromOptions(),
                    isSearch: isSearch,
                    isTextarea: isTextarea,
                    isRange: isRange,
                    inputNeedsWrap: inputNeedsWrap
                });

                this._autoCorrect();

                if (!options.enhanced) {
                    this._enhance();
                }

                this._on({
                    "focus": "_handleFocus",
                    "blur": "_handleBlur"
                });

            },

            refresh: function () {
                this.setOptions({
                    "disabled": this.element.is(":disabled")
                });
            },

            _enhance: function () {
                var elementClasses = [];

                if (this.isTextarea) {
                    elementClasses.push("ui-input-text");
                }

                if (this.isTextarea || this.isRange) {
                    elementClasses.push("ui-shadow-inset");
                }

                //"search" and "text" input widgets
                if (this.inputNeedsWrap) {
                    this.element.wrap(this._wrap());
                } else {
                    elementClasses = elementClasses.concat(this.classes);
                }

                this.element.addClass(elementClasses.join(" "));
            },

            widget: function () {
                return (this.inputNeedsWrap) ? this.element.parent() : this.element;
            },

            _classesFromOptions: function () {
                var options = this.options,
                    classes = [];

                classes.push("ui-body-" + ((options.theme === null) ? "inherit" : options.theme));
                if (options.corners) {
                    classes.push("ui-corner-all");
                }
                if (options.mini) {
                    classes.push("ui-mini");
                }
                if (options.disabled) {
                    classes.push("ui-state-disabled");
                }
                if (options.wrapperClass) {
                    classes.push(options.wrapperClass);
                }

                return classes;
            },

            _wrap: function () {
                return $("<div class='" +
                    (this.isSearch ? "ui-input-search " : "ui-input-text ") +
                    this.classes.join(" ") + " " +
                    "ui-shadow-inset'></div>");
            },

            _autoCorrect: function () {
                // XXX: Temporary workaround for issue 785 (Apple bug 8910589).
                //      Turn off autocorrect and autocomplete on non-iOS 5 devices
                //      since the popup they use can't be dismissed by the user. Note
                //      that we test for the presence of the feature by looking for
                //      the autocorrect property on the input element. We currently
                //      have no test for iOS 5 or newer so we're temporarily using
                //      the touchOverflow support flag for jQM 1.0. Yes, I feel dirty.
                //      - jblas
                if (typeof this.element[0].autocorrect !== "undefined" &&
                    !$.support.touchOverflow) {

                    // Set the attribute instead of the property just in case there
                    // is code that attempts to make modifications via HTML.
                    this.element[0].setAttribute("autocorrect", "off");
                    this.element[0].setAttribute("autocomplete", "off");
                }
            },

            _handleBlur: function () {
                this.widget().removeClass($.mobile.focusClass);
                if (this.options.preventFocusZoom) {
                    $.mobile.zoom.enable(true);
                }
            },

            _handleFocus: function () {
                // In many situations, iOS will zoom into the input upon tap, this
                // prevents that from happening
                if (this.options.preventFocusZoom) {
                    $.mobile.zoom.disable(true);
                }
                this.widget().addClass($.mobile.focusClass);
            },

            _setOptions: function (options) {
                var outer = this.widget();

                this._super(options);

                if (!(options.disabled === undefined &&
                    options.mini === undefined &&
                    options.corners === undefined &&
                    options.theme === undefined &&
                    options.wrapperClass === undefined)) {

                    outer.removeClass(this.classes.join(" "));
                    this.classes = this._classesFromOptions();
                    outer.addClass(this.classes.join(" "));
                }

                if (options.disabled !== undefined) {
                    this.element.prop("disabled", !!options.disabled);
                }
            },

            _destroy: function () {
                if (this.options.enhanced) {
                    return;
                }
                if (this.inputNeedsWrap) {
                    this.element.unwrap();
                }
                this.element.removeClass("ui-input-text " + this.classes.join(" "));
            }
        });

    })(jQuery);

    (function ($, undefined) {

        $.widget("mobile.textinput", $.mobile.textinput, {
            options: {
                autogrow: true,
                keyupTimeoutBuffer: 100
            },

            _create: function () {
                this._super();

                if (this.options.autogrow && this.isTextarea) {
                    this._autogrow();
                }
            },

            _autogrow: function () {
                this.element.addClass("ui-textinput-autogrow");

                this._on({
                    "keyup": "_timeout",
                    "change": "_timeout",
                    "input": "_timeout",
                    "paste": "_timeout"
                });

                // Attach to the various you-have-become-visible notifications that the
                // various framework elements emit.
                // TODO: Remove all but the updatelayout handler once #6426 is fixed.
                this._on(true, this.document, {

                    // TODO: Move to non-deprecated event
                    "pageshow": "_handleShow",
                    "popupbeforeposition": "_handleShow",
                    "updatelayout": "_handleShow",
                    "panelopen": "_handleShow"
                });
            },

            // Synchronously fix the widget height if this widget's parents are such
            // that they show/hide content at runtime. We still need to check whether
            // the widget is actually visible in case it is contained inside multiple
            // such containers. For example: panel contains collapsible contains
            // autogrow textinput. The panel may emit "panelopen" indicating that its
            // content has become visible, but the collapsible is still collapsed, so
            // the autogrow textarea is still not visible.
            _handleShow: function (event) {
                if ($.contains(event.target, this.element[0]) &&
                    this.element.is(":visible")) {

                    if (event.type !== "popupbeforeposition") {
                        this.element
                            .addClass("ui-textinput-autogrow-resize")
                            .animationComplete(
                                $.proxy(function () {
                                    this.element.removeClass("ui-textinput-autogrow-resize");
                                }, this),
                            "transition");
                    }
                    this._prepareHeightUpdate();
                }
            },

            _unbindAutogrow: function () {
                this.element.removeClass("ui-textinput-autogrow");
                this._off(this.element, "keyup change input paste");
                this._off(this.document,
                    "pageshow popupbeforeposition updatelayout panelopen");
            },

            keyupTimeout: null,

            _prepareHeightUpdate: function (delay) {
                if (this.keyupTimeout) {
                    clearTimeout(this.keyupTimeout);
                }
                if (delay === undefined) {
                    this._updateHeight();
                } else {
                    this.keyupTimeout = this._delay("_updateHeight", delay);
                }
            },

            _timeout: function () {
                this._prepareHeightUpdate(this.options.keyupTimeoutBuffer);
            },

            _updateHeight: function () {
                var paddingTop, paddingBottom, paddingHeight, scrollHeight, clientHeight,
                    borderTop, borderBottom, borderHeight, height,
                    scrollTop = this.window.scrollTop();
                this.keyupTimeout = 0;

                // IE8 textareas have the onpage property - others do not
                if (!("onpage" in this.element[0])) {
                    this.element.css({
                        "height": 0,
                        "min-height": 0,
                        "max-height": 0
                    });
                }

                scrollHeight = this.element[0].scrollHeight;
                clientHeight = this.element[0].clientHeight;
                borderTop = parseFloat(this.element.css("border-top-width"));
                borderBottom = parseFloat(this.element.css("border-bottom-width"));
                borderHeight = borderTop + borderBottom;
                height = scrollHeight + borderHeight + 15;

                // Issue 6179: Padding is not included in scrollHeight and
                // clientHeight by Firefox if no scrollbar is visible. Because
                // textareas use the border-box box-sizing model, padding should be
                // included in the new (assigned) height. Because the height is set
                // to 0, clientHeight == 0 in Firefox. Therefore, we can use this to
                // check if padding must be added.
                if (clientHeight === 0) {
                    paddingTop = parseFloat(this.element.css("padding-top"));
                    paddingBottom = parseFloat(this.element.css("padding-bottom"));
                    paddingHeight = paddingTop + paddingBottom;

                    height += paddingHeight;
                }

                this.element.css({
                    "height": height,
                    "min-height": "",
                    "max-height": ""
                });

                this.window.scrollTop(scrollTop);
            },

            refresh: function () {
                if (this.options.autogrow && this.isTextarea) {
                    this._updateHeight();
                }
            },

            _setOptions: function (options) {

                this._super(options);

                if (options.autogrow !== undefined && this.isTextarea) {
                    if (options.autogrow) {
                        this._autogrow();
                    } else {
                        this._unbindAutogrow();
                    }
                }
            }

        });
    })(jQuery);

    (function ($, undefined) {

        $.widget("mobile.button", {

            initSelector: "input[type='button'], input[type='submit'], input[type='reset']",

            options: {
                theme: null,
                icon: null,
                iconpos: "left",
                iconshadow: false, /* TODO: Deprecated in 1.4, remove in 1.5. */
                corners: true,
                shadow: true,
                inline: null,
                mini: null,
                wrapperClass: null,
                enhanced: false
            },

            _create: function () {

                if (this.element.is(":disabled")) {
                    this.options.disabled = true;
                }

                if (!this.options.enhanced) {
                    this._enhance();
                }

                $.extend(this, {
                    wrapper: this.element.parent()
                });

                this._on({
                    focus: function () {
                        this.widget().addClass($.mobile.focusClass);
                    },

                    blur: function () {
                        this.widget().removeClass($.mobile.focusClass);
                    }
                });

                this.refresh(true);
            },

            _enhance: function () {
                this.element.wrap(this._button());
            },

            _button: function () {
                var options = this.options,
                    iconClasses = this._getIconClasses(this.options);

                return $("<div class='ui-btn ui-input-btn" +
                    (options.wrapperClass ? " " + options.wrapperClass : "") +
                    (options.theme ? " ui-btn-" + options.theme : "") +
                    (options.corners ? " ui-corner-all" : "") +
                    (options.shadow ? " ui-shadow" : "") +
                    (options.inline ? " ui-btn-inline" : "") +
                    (options.mini ? " ui-mini" : "") +
                    (options.disabled ? " ui-state-disabled" : "") +
                    (iconClasses ? (" " + iconClasses) : "") +
                    "' >" + this.element.val() + "</div>");
            },

            widget: function () {
                return this.wrapper;
            },

            _destroy: function () {
                this.element.insertBefore(this.wrapper);
                this.wrapper.remove();
            },

            _getIconClasses: function (options) {
                return (options.icon ? ("ui-icon-" + options.icon +
                    (options.iconshadow ? " ui-shadow-icon" : "") + /* TODO: Deprecated in 1.4, remove in 1.5. */
                    " ui-btn-icon-" + options.iconpos) : "");
            },

            _setOptions: function (options) {
                var outer = this.widget();

                if (options.theme !== undefined) {
                    outer
                        .removeClass(this.options.theme)
                        .addClass("ui-btn-" + options.theme);
                }
                if (options.corners !== undefined) {
                    outer.toggleClass("ui-corner-all", options.corners);
                }
                if (options.shadow !== undefined) {
                    outer.toggleClass("ui-shadow", options.shadow);
                }
                if (options.inline !== undefined) {
                    outer.toggleClass("ui-btn-inline", options.inline);
                }
                if (options.mini !== undefined) {
                    outer.toggleClass("ui-mini", options.mini);
                }
                if (options.disabled !== undefined) {
                    this.element.prop("disabled", options.disabled);
                    outer.toggleClass("ui-state-disabled", options.disabled);
                }

                if (options.icon !== undefined ||
                        options.iconshadow !== undefined || /* TODO: Deprecated in 1.4, remove in 1.5. */
                        options.iconpos !== undefined) {
                    outer
                        .removeClass(this._getIconClasses(this.options))
                        .addClass(this._getIconClasses(
                            $.extend({}, this.options, options)));
                }

                this._super(options);
            },

            refresh: function (create) {
                var originalElement,
                    isDisabled = this.element.prop("disabled");

                if (this.options.icon && this.options.iconpos === "notext" && this.element.attr("title")) {
                    this.element.attr("title", this.element.val());
                }
                if (!create) {
                    originalElement = this.element.detach();
                    $(this.wrapper).text(this.element.val()).append(originalElement);
                }
                if (this.options.disabled !== isDisabled) {
                    this._setOptions({ disabled: isDisabled });
                }
            }
        });

    })(jQuery);

    (function ($, undefined) {

        $.mobile.behaviors.formReset = {
            _handleFormReset: function () {
                this._on(this.element.closest("form"), {
                    reset: function () {
                        this._delay("_reset");
                    }
                });
            }
        };

    })(jQuery);

    /*
    * "checkboxradio" plugin
    */

    (function ($, undefined) {

        var escapeId = $.mobile.path.hashToSelector;

        $.widget("mobile.checkboxradio", $.extend({

            initSelector: "input:not( :jqmData(role='flipswitch' ) )[type='checkbox'],input[type='radio']:not( :jqmData(role='flipswitch' ))",

            options: {
                theme: "inherit",
                mini: false,
                wrapperClass: null,
                enhanced: false,
                iconpos: "left"

            },
            _create: function () {
                var input = this.element,
                    o = this.options,
                    inheritAttr = function (input, dataAttr) {
                        return input.jqmData(dataAttr) ||
                            input.closest("form, fieldset").jqmData(dataAttr);
                    },
                    label = this.options.enhanced ?
				{
				    element: this.element.siblings("label"),
				    isParent: false
				} :
                        this._findLabel(),
                    inputtype = input[0].type,
                    checkedClass = "ui-" + inputtype + "-on",
                    uncheckedClass = "ui-" + inputtype + "-off";

                if (inputtype !== "checkbox" && inputtype !== "radio") {
                    return;
                }

                if (this.element[0].disabled) {
                    this.options.disabled = true;
                }

                o.iconpos = inheritAttr(input, "iconpos") ||
                    label.element.attr("data-" + $.mobile.ns + "iconpos") || o.iconpos,

                // Establish options
                o.mini = inheritAttr(input, "mini") || o.mini;

                // Expose for other methods
                $.extend(this, {
                    input: input,
                    label: label.element,
                    labelIsParent: label.isParent,
                    inputtype: inputtype,
                    checkedClass: checkedClass,
                    uncheckedClass: uncheckedClass
                });

                if (!this.options.enhanced) {
                    this._enhance();
                }

                this._on(label.element, {
                    vmouseover: "_handleLabelVMouseOver",
                    vclick: "_handleLabelVClick"
                });

                this._on(input, {
                    vmousedown: "_cacheVals",
                    vclick: "_handleInputVClick",
                    focus: "_handleInputFocus",
                    blur: "_handleInputBlur"
                });

                this._handleFormReset();
                this.refresh();
            },

            _findLabel: function () {
                var parentLabel, label, isParent,
                    input = this.element,
                    labelsList = input[0].labels;

                if (labelsList && labelsList.length > 0) {
                    label = $(labelsList[0]);
                    isParent = $.contains(label[0], input[0]);
                } else {
                    parentLabel = input.closest("label");
                    isParent = (parentLabel.length > 0);

                    // NOTE: Windows Phone could not find the label through a selector
                    // filter works though.
                    label = isParent ? parentLabel :
                        $(this.document[0].getElementsByTagName("label"))
                            .filter("[for='" + escapeId(input[0].id) + "']")
                            .first();
                }

                return {
                    element: label,
                    isParent: isParent
                };
            },

            _enhance: function () {
                this.label.addClass("ui-btn ui-corner-all");

                if (this.labelIsParent) {
                    this.input.add(this.label).wrapAll(this._wrapper());
                } else {
                    //this.element.replaceWith( this.input.add( this.label ).wrapAll( this._wrapper() ) );
                    this.element.wrap(this._wrapper());
                    this.element.parent().prepend(this.label);
                }

                // Wrap the input + label in a div

                this._setOptions({
                    "theme": this.options.theme,
                    "iconpos": this.options.iconpos,
                    "mini": this.options.mini
                });

            },

            _wrapper: function () {
                return $("<div class='" +
                    (this.options.wrapperClass ? this.options.wrapperClass : "") +
                    " ui-" + this.inputtype +
                    (this.options.disabled ? " ui-state-disabled" : "") + "' ></div>");
            },

            _handleInputFocus: function () {
                this.label.addClass($.mobile.focusClass);
            },

            _handleInputBlur: function () {
                this.label.removeClass($.mobile.focusClass);
            },

            _handleInputVClick: function () {
                // Adds checked attribute to checked input when keyboard is used
                this.element.prop("checked", this.element.is(":checked"));
                this._getInputSet().not(this.element).prop("checked", false);
                this._updateAll(true);
            },

            _handleLabelVMouseOver: function (event) {
                if (this.label.parent().hasClass("ui-state-disabled")) {
                    event.stopPropagation();
                }
            },

            _handleLabelVClick: function (event) {
                var input = this.element;

                if (input.is(":disabled")) {
                    event.preventDefault();
                    return;
                }

                this._cacheVals();

                input.prop("checked", this.inputtype === "radio" && true || !input.prop("checked"));

                // trigger click handler's bound directly to the input as a substitute for
                // how label clicks behave normally in the browsers
                // TODO: it would be nice to let the browser's handle the clicks and pass them
                //       through to the associate input. we can swallow that click at the parent
                //       wrapper element level
                input.triggerHandler("click");

                // Input set for common radio buttons will contain all the radio
                // buttons, but will not for checkboxes. clearing the checked status
                // of other radios ensures the active button state is applied properly
                this._getInputSet().not(input).prop("checked", false);

                this._updateAll();
                return false;
            },

            _cacheVals: function () {
                this._getInputSet().each(function () {
                    $(this).attr("data-" + $.mobile.ns + "cacheVal", this.checked);
                });
            },

            // Returns those radio buttons that are supposed to be in the same group as
            // this radio button. In the case of a checkbox or a radio lacking a name
            // attribute, it returns this.element.
            _getInputSet: function () {
                var selector, formId,
                    radio = this.element[0],
                    name = radio.name,
                    form = radio.form,
                    doc = this.element.parents().last().get(0),

                    // A radio is always a member of its own group
                    radios = this.element;

                // Only start running selectors if this is an attached radio button with a name
                if (name && this.inputtype === "radio" && doc) {
                    selector = "input[type='radio'][name='" + escapeId(name) + "']";

                    // If we're inside a form
                    if (form) {
                        formId = form.getAttribute("id");

                        // If the form has an ID, collect radios scattered throught the document which
                        // nevertheless are part of the form by way of the value of their form attribute
                        if (formId) {
                            radios = $(selector + "[form='" + escapeId(formId) + "']", doc);
                        }

                        // Also add to those the radios in the form itself
                        radios = $(form).find(selector).filter(function () {

                            // Some radios inside the form may belong to some other form by virtue of
                            // having a form attribute defined on them, so we must filter them out here
                            return (this.form === form);
                        }).add(radios);

                        // If we're outside a form
                    } else {

                        // Collect all those radios which are also outside of a form and match our name
                        radios = $(selector, doc).filter(function () {
                            return !this.form;
                        });
                    }
                }
                return radios;
            },

            _updateAll: function (changeTriggered) {
                var self = this;

                this._getInputSet().each(function () {
                    var $this = $(this);

                    if ((this.checked || self.inputtype === "checkbox") && !changeTriggered) {
                        $this.trigger("change");
                    }
                })
                .checkboxradio("refresh");
            },

            _reset: function () {
                this.refresh();
            },

            // Is the widget supposed to display an icon?
            _hasIcon: function () {
                var controlgroup, controlgroupWidget,
                    controlgroupConstructor = $.mobile.controlgroup;

                // If the controlgroup widget is defined ...
                if (controlgroupConstructor) {
                    controlgroup = this.element.closest(
                        ":mobile-controlgroup," +
                        controlgroupConstructor.prototype.initSelector);

                    // ... and the checkbox is in a controlgroup ...
                    if (controlgroup.length > 0) {

                        // ... look for a controlgroup widget instance, and ...
                        controlgroupWidget = $.data(controlgroup[0], "mobile-controlgroup");

                        // ... if found, decide based on the option value, ...
                        return ((controlgroupWidget ? controlgroupWidget.options.type :

                            // ... otherwise decide based on the "type" data attribute.
                            controlgroup.attr("data-" + $.mobile.ns + "type")) !== "horizontal");
                    }
                }

                // Normally, the widget displays an icon.
                return true;
            },

            refresh: function () {
                var isChecked = this.element[0].checked,
                    active = $.mobile.activeBtnClass,
                    iconposClass = "ui-btn-icon-" + this.options.iconpos,
                    addClasses = [],
                    removeClasses = [];

                if (this._hasIcon()) {
                    removeClasses.push(active);
                    addClasses.push(iconposClass);
                } else {
                    removeClasses.push(iconposClass);
                    (isChecked ? addClasses : removeClasses).push(active);
                }

                if (isChecked) {
                    addClasses.push(this.checkedClass);
                    removeClasses.push(this.uncheckedClass);
                } else {
                    addClasses.push(this.uncheckedClass);
                    removeClasses.push(this.checkedClass);
                }

                this.widget().toggleClass("ui-state-disabled", this.element.prop("disabled"));

                this.label
                    .addClass(addClasses.join(" "))
                    .removeClass(removeClasses.join(" "));
            },

            widget: function () {
                return this.label.parent();
            },

            _setOptions: function (options) {
                var label = this.label,
                    currentOptions = this.options,
                    outer = this.widget(),
                    hasIcon = this._hasIcon();

                if (options.disabled !== undefined) {
                    this.input.prop("disabled", !!options.disabled);
                    outer.toggleClass("ui-state-disabled", !!options.disabled);
                }
                if (options.mini !== undefined) {
                    outer.toggleClass("ui-mini", !!options.mini);
                }
                if (options.theme !== undefined) {
                    label
                        .removeClass("ui-btn-" + currentOptions.theme)
                        .addClass("ui-btn-" + options.theme);
                }
                if (options.wrapperClass !== undefined) {
                    outer
                        .removeClass(currentOptions.wrapperClass)
                        .addClass(options.wrapperClass);
                }
                if (options.iconpos !== undefined && hasIcon) {
                    label.removeClass("ui-btn-icon-" + currentOptions.iconpos).addClass("ui-btn-icon-" + options.iconpos);
                } else if (!hasIcon) {
                    label.removeClass("ui-btn-icon-" + currentOptions.iconpos);
                }
                this._super(options);
            }

        }, $.mobile.behaviors.formReset));

    })(jQuery);

    (function ($, undefined) {

        $.widget("mobile.textinput", $.mobile.textinput, {
            options: {
                clearBtn: false,
                clearBtnText: "Clear text"
            },

            _create: function () {
                this._super();

                if (this.isSearch) {
                    this.options.clearBtn = true;
                }

                if (!!this.options.clearBtn && this.inputNeedsWrap) {
                    this._addClearBtn();
                }
            },

            clearButton: function () {
                return $("<a href='#' tabindex='-1' aria-hidden='true' " +
                    "class='ui-input-clear ui-btn ui-icon-delete ui-btn-icon-notext ui-corner-all'>" +
                    "</a>")
                        .attr("title", this.options.clearBtnText)
                        .text(this.options.clearBtnText);
            },

            _clearBtnClick: function (event) {
                this.element.val("")
                        .focus()
                        .trigger("change");

                this._clearBtn.addClass("ui-input-clear-hidden");
                event.preventDefault();
            },

            _addClearBtn: function () {

                if (!this.options.enhanced) {
                    this._enhanceClear();
                }

                $.extend(this, {
                    _clearBtn: this.widget().find("a.ui-input-clear")
                });

                this._bindClearEvents();

                this._toggleClear();

            },

            _enhanceClear: function () {

                this.clearButton().appendTo(this.widget());
                this.widget().addClass("ui-input-has-clear");

            },

            _bindClearEvents: function () {

                this._on(this._clearBtn, {
                    "click": "_clearBtnClick"
                });

                this._on({
                    "keyup": "_toggleClear",
                    "change": "_toggleClear",
                    "input": "_toggleClear",
                    "focus": "_toggleClear",
                    "blur": "_toggleClear",
                    "cut": "_toggleClear",
                    "paste": "_toggleClear"

                });

            },

            _unbindClear: function () {
                this._off(this._clearBtn, "click");
                this._off(this.element, "keyup change input focus blur cut paste");
            },

            _setOptions: function (options) {
                this._super(options);

                if (options.clearBtn !== undefined &&
                    !this.element.is("textarea, :jqmData(type='range')")) {
                    if (options.clearBtn) {
                        this._addClearBtn();
                    } else {
                        this._destroyClear();
                    }
                }

                if (options.clearBtnText !== undefined && this._clearBtn !== undefined) {
                    this._clearBtn.text(options.clearBtnText)
                        .attr("title", options.clearBtnText);
                }
            },

            _toggleClear: function () {
                this._delay("_toggleClearClass", 0);
            },

            _toggleClearClass: function () {
                this._clearBtn.toggleClass("ui-input-clear-hidden", !this.element.val());
            },

            _destroyClear: function () {
                this.widget().removeClass("ui-input-has-clear");
                this._unbindClear();
                this._clearBtn.remove();
            },

            _destroy: function () {
                this._super();
                if (this.options.clearBtn) {
                    this._destroyClear();
                }
            }

        });

    })(jQuery);

    (function ($, undefined) {

        $.widget("mobile.flipswitch", $.extend({

            options: {
                onText: "On",
                offText: "Off",
                theme: null,
                enhanced: false,
                wrapperClass: null,
                corners: true,
                mini: false
            },

            _create: function () {
                if (!this.options.enhanced) {
                    this._enhance();
                } else {
                    $.extend(this, {
                        flipswitch: this.element.parent(),
                        on: this.element.find(".ui-flipswitch-on").eq(0),
                        off: this.element.find(".ui-flipswitch-off").eq(0),
                        type: this.element.get(0).tagName
                    });
                }

                this._handleFormReset();

                // Transfer tabindex to "on" element and make input unfocusable
                this._originalTabIndex = this.element.attr("tabindex");
                if (this._originalTabIndex != null) {
                    this.on.attr("tabindex", this._originalTabIndex);
                }
                this.element.attr("tabindex", "-1");
                this._on({
                    "focus": "_handleInputFocus"
                });

                if (this.element.is(":disabled")) {
                    this._setOptions({
                        "disabled": true
                    });
                }

                this._on(this.flipswitch, {
                    "click": "_toggle",
                    "swipeleft": "_left",
                    "swiperight": "_right"
                });

                this._on(this.on, {
                    "keydown": "_keydown"
                });

                this._on({
                    "change": "refresh"
                });
            },

            _handleInputFocus: function () {
                this.on.focus();
            },

            widget: function () {
                return this.flipswitch;
            },

            _left: function () {
                this.flipswitch.removeClass("ui-flipswitch-active");
                if (this.type === "SELECT") {
                    this.element.get(0).selectedIndex = 0;
                } else {
                    this.element.prop("checked", false);
                }
                this.element.trigger("change");
            },

            _right: function () {
                this.flipswitch.addClass("ui-flipswitch-active");
                if (this.type === "SELECT") {
                    this.element.get(0).selectedIndex = 1;
                } else {
                    this.element.prop("checked", true);
                }
                this.element.trigger("change");
            },

            _enhance: function () {
                var flipswitch = $("<div>"),
                    options = this.options,
                    element = this.element,
                    theme = options.theme ? options.theme : "inherit",

                    // The "on" button is an anchor so it's focusable
                    on = $("<a></a>", {
                        "href": "#"
                    }),
                    off = $("<span></span>"),
                    type = element.get(0).tagName,
                    onText = (type === "INPUT") ?
                        options.onText : element.find("option").eq(1).text(),
                    offText = (type === "INPUT") ?
                        options.offText : element.find("option").eq(0).text();

                on
                    .addClass("ui-flipswitch-on ui-btn ui-shadow ui-btn-inherit")
                    .text(onText);
                off
                    .addClass("ui-flipswitch-off")
                    .text(offText);

                flipswitch
                    .addClass("ui-flipswitch ui-shadow-inset " +
                        "ui-bar-" + theme + " " +
                        (options.wrapperClass ? options.wrapperClass : "") + " " +
                        ((element.is(":checked") ||
                            element
                                .find("option")
                                .eq(1)
                                .is(":selected")) ? "ui-flipswitch-active" : "") +
                        (element.is(":disabled") ? " ui-state-disabled" : "") +
                        (options.corners ? " ui-corner-all" : "") +
                        (options.mini ? " ui-mini" : ""))
                    .append(on, off);

                element
                    .addClass("ui-flipswitch-input")
                    .after(flipswitch)
                    .appendTo(flipswitch);

                $.extend(this, {
                    flipswitch: flipswitch,
                    on: on,
                    off: off,
                    type: type
                });
            },

            _reset: function () {
                this.refresh();
            },

            refresh: function () {
                var direction,
                    existingDirection = this.flipswitch.hasClass("ui-flipswitch-active") ? "_right" : "_left";

                if (this.type === "SELECT") {
                    direction = (this.element.get(0).selectedIndex > 0) ? "_right" : "_left";
                } else {
                    direction = this.element.prop("checked") ? "_right" : "_left";
                }

                if (direction !== existingDirection) {
                    this[direction]();
                }
            },

            _toggle: function () {
                var direction = this.flipswitch.hasClass("ui-flipswitch-active") ? "_left" : "_right";

                this[direction]();
            },

            _keydown: function (e) {
                if (e.which === $.mobile.keyCode.LEFT) {
                    this._left();
                } else if (e.which === $.mobile.keyCode.RIGHT) {
                    this._right();
                } else if (e.which === $.mobile.keyCode.SPACE) {
                    this._toggle();
                    e.preventDefault();
                }
            },

            _setOptions: function (options) {
                if (options.theme !== undefined) {
                    var currentTheme = options.theme ? options.theme : "inherit",
                        newTheme = options.theme ? options.theme : "inherit";

                    this.widget()
                        .removeClass("ui-bar-" + currentTheme)
                        .addClass("ui-bar-" + newTheme);
                }
                if (options.onText !== undefined) {
                    this.on.text(options.onText);
                }
                if (options.offText !== undefined) {
                    this.off.text(options.offText);
                }
                if (options.disabled !== undefined) {
                    this.widget().toggleClass("ui-state-disabled", options.disabled);
                }
                if (options.mini !== undefined) {
                    this.widget().toggleClass("ui-mini", options.mini);
                }
                if (options.corners !== undefined) {
                    this.widget().toggleClass("ui-corner-all", options.corners);
                }

                this._super(options);
            },

            _destroy: function () {
                if (this.options.enhanced) {
                    return;
                }
                if (this._originalTabIndex != null) {
                    this.element.attr("tabindex", this._originalTabIndex);
                } else {
                    this.element.removeAttr("tabindex");
                }
                this.on.remove();
                this.off.remove();
                this.element.unwrap();
                this.flipswitch.remove();
                this.removeClass("ui-flipswitch-input");
            }

        }, $.mobile.behaviors.formReset));

    })(jQuery);

    (function ($, undefined) {

        $.widget("mobile.slider", $.extend({
            initSelector: "input[type='range'], :jqmData(type='range'), :jqmData(role='slider')",

            widgetEventPrefix: "slide",

            options: {
                theme: null,
                trackTheme: null,
                corners: true,
                mini: false,
                highlight: false
            },

            _create: function () {

                // TODO: Each of these should have comments explain what they're for
                var self = this,
                    control = this.element,
                    trackTheme = this.options.trackTheme || $.mobile.getAttribute(control[0], "theme"),
                    trackThemeClass = trackTheme ? " ui-bar-" + trackTheme : " ui-bar-inherit",
                    cornerClass = (this.options.corners || control.jqmData("corners")) ? " ui-corner-all" : "",
                    miniClass = (this.options.mini || control.jqmData("mini")) ? " ui-mini" : "",
                    cType = control[0].nodeName.toLowerCase(),
                    isToggleSwitch = (cType === "select"),
                    isRangeslider = control.parent().is(":jqmData(role='rangeslider')"),
                    selectClass = (isToggleSwitch) ? "ui-slider-switch" : "",
                    controlID = control.attr("id"),
                    $label = $("[for='" + controlID + "']"),
                    labelID = $label.attr("id") || controlID + "-label",
                    min = !isToggleSwitch ? parseFloat(control.attr("min")) : 0,
                    max = !isToggleSwitch ? parseFloat(control.attr("max")) : control.find("option").length - 1,
                    step = window.parseFloat(control.attr("step") || 1),
                    domHandle = document.createElement("a"),
                    handle = $(domHandle),
                    domSlider = document.createElement("div"),
                    slider = $(domSlider),
                    valuebg = this.options.highlight && !isToggleSwitch ? (function () {
                        var bg = document.createElement("div");
                        bg.className = "ui-slider-bg " + $.mobile.activeBtnClass;
                        return $(bg).prependTo(slider);
                    })() : false,
                    options,
                    wrapper,
                    j, length,
                    i, optionsCount, origTabIndex,
                    side, activeClass, sliderImg;

                $label.attr("id", labelID);
                this.isToggleSwitch = isToggleSwitch;

                domHandle.setAttribute("href", "#");
                domSlider.setAttribute("role", "application");
                domSlider.className = [this.isToggleSwitch ? "ui-slider ui-slider-track ui-shadow-inset " : "ui-slider-track ui-shadow-inset ", selectClass, trackThemeClass, cornerClass, miniClass].join("");
                domHandle.className = "ui-slider-handle";
                domSlider.appendChild(domHandle);

                handle.attr({
                    "role": "slider",
                    "aria-valuemin": min,
                    "aria-valuemax": max,
                    "aria-valuenow": this._value(),
                    "aria-valuetext": this._value(),
                    "title": this._value(),
                    "aria-labelledby": labelID
                });

                $.extend(this, {
                    slider: slider,
                    handle: handle,
                    control: control,
                    type: cType,
                    step: step,
                    max: max,
                    min: min,
                    valuebg: valuebg,
                    isRangeslider: isRangeslider,
                    dragging: false,
                    beforeStart: null,
                    userModified: false,
                    mouseMoved: false
                });

                if (isToggleSwitch) {
                    // TODO: restore original tabindex (if any) in a destroy method
                    origTabIndex = control.attr("tabindex");
                    if (origTabIndex) {
                        handle.attr("tabindex", origTabIndex);
                    }
                    control.attr("tabindex", "-1").focus(function () {
                        $(this).blur();
                        handle.focus();
                    });

                    wrapper = document.createElement("div");
                    wrapper.className = "ui-slider-inneroffset";

                    for (j = 0, length = domSlider.childNodes.length; j < length; j++) {
                        wrapper.appendChild(domSlider.childNodes[j]);
                    }

                    domSlider.appendChild(wrapper);

                    // slider.wrapInner( "<div class='ui-slider-inneroffset'></div>" );

                    // make the handle move with a smooth transition
                    handle.addClass("ui-slider-handle-snapping");

                    options = control.find("option");

                    for (i = 0, optionsCount = options.length; i < optionsCount; i++) {
                        side = !i ? "b" : "a";
                        activeClass = !i ? "" : " " + $.mobile.activeBtnClass;
                        sliderImg = document.createElement("span");

                        sliderImg.className = ["ui-slider-label ui-slider-label-", side, activeClass].join("");
                        sliderImg.setAttribute("role", "img");
                        sliderImg.appendChild(document.createTextNode(options[i].innerHTML));
                        $(sliderImg).prependTo(slider);
                    }

                    self._labels = $(".ui-slider-label", slider);

                }

                // monitor the input for updated values
                control.addClass(isToggleSwitch ? "ui-slider-switch" : "ui-slider-input");

                this._on(control, {
                    "change": "_controlChange",
                    "keyup": "_controlKeyup",
                    "blur": "_controlBlur",
                    "vmouseup": "_controlVMouseUp"
                });

                slider.bind("vmousedown", $.proxy(this._sliderVMouseDown, this))
                    .bind("vclick", false);

                // We have to instantiate a new function object for the unbind to work properly
                // since the method itself is defined in the prototype (causing it to unbind everything)
                this._on(document, { "vmousemove": "_preventDocumentDrag" });
                this._on(slider.add(document), { "vmouseup": "_sliderVMouseUp" });

                slider.insertAfter(control);

                // wrap in a div for styling purposes
                if (!isToggleSwitch && !isRangeslider) {
                    wrapper = this.options.mini ? "<div class='ui-slider ui-mini'>" : "<div class='ui-slider'>";

                    control.add(slider).wrapAll(wrapper);
                }

                // bind the handle event callbacks and set the context to the widget instance
                this._on(this.handle, {
                    "vmousedown": "_handleVMouseDown",
                    "keydown": "_handleKeydown",
                    "keyup": "_handleKeyup"
                });

                this.handle.bind("vclick", false);

                this._handleFormReset();

                this.refresh(undefined, undefined, true);
            },

            _setOptions: function (options) {
                if (options.theme !== undefined) {
                    this._setTheme(options.theme);
                }

                if (options.trackTheme !== undefined) {
                    this._setTrackTheme(options.trackTheme);
                }

                if (options.corners !== undefined) {
                    this._setCorners(options.corners);
                }

                if (options.mini !== undefined) {
                    this._setMini(options.mini);
                }

                if (options.highlight !== undefined) {
                    this._setHighlight(options.highlight);
                }

                if (options.disabled !== undefined) {
                    this._setDisabled(options.disabled);
                }
                this._super(options);
            },

            _controlChange: function (event) {
                // if the user dragged the handle, the "change" event was triggered from inside refresh(); don't call refresh() again
                if (this._trigger("controlchange", event) === false) {
                    return false;
                }
                if (!this.mouseMoved) {
                    this.refresh(this._value(), true);
                }
            },

            _controlKeyup: function (/* event */) { // necessary?
                this.refresh(this._value(), true, true);
            },

            _controlBlur: function (/* event */) {
                this.refresh(this._value(), true);
            },

            // it appears the clicking the up and down buttons in chrome on
            // range/number inputs doesn't trigger a change until the field is
            // blurred. Here we check thif the value has changed and refresh
            _controlVMouseUp: function (/* event */) {
                this._checkedRefresh();
            },

            // NOTE force focus on handle
            _handleVMouseDown: function (/* event */) {
                this.handle.focus();
            },

            _handleKeydown: function (event) {
                var index = this._value();
                if (this.options.disabled) {
                    return;
                }

                // In all cases prevent the default and mark the handle as active
                switch (event.keyCode) {
                    case $.mobile.keyCode.HOME:
                    case $.mobile.keyCode.END:
                    case $.mobile.keyCode.PAGE_UP:
                    case $.mobile.keyCode.PAGE_DOWN:
                    case $.mobile.keyCode.UP:
                    case $.mobile.keyCode.RIGHT:
                    case $.mobile.keyCode.DOWN:
                    case $.mobile.keyCode.LEFT:
                        event.preventDefault();

                        if (!this._keySliding) {
                            this._keySliding = true;
                            this.handle.addClass("ui-state-active"); /* TODO: We don't use this class for styling. Do we need to add it? */
                        }

                        break;
                }

                // move the slider according to the keypress
                switch (event.keyCode) {
                    case $.mobile.keyCode.HOME:
                        this.refresh(this.min);
                        break;
                    case $.mobile.keyCode.END:
                        this.refresh(this.max);
                        break;
                    case $.mobile.keyCode.PAGE_UP:
                    case $.mobile.keyCode.UP:
                    case $.mobile.keyCode.RIGHT:
                        this.refresh(index + this.step);
                        break;
                    case $.mobile.keyCode.PAGE_DOWN:
                    case $.mobile.keyCode.DOWN:
                    case $.mobile.keyCode.LEFT:
                        this.refresh(index - this.step);
                        break;
                }
            }, // remove active mark

            _handleKeyup: function (/* event */) {
                if (this._keySliding) {
                    this._keySliding = false;
                    this.handle.removeClass("ui-state-active"); /* See comment above. */
                }
            },

            _sliderVMouseDown: function (event) {
                // NOTE: we don't do this in refresh because we still want to
                //       support programmatic alteration of disabled inputs
                if (this.options.disabled || !(event.which === 1 || event.which === 0 || event.which === undefined)) {
                    return false;
                }
                if (this._trigger("beforestart", event) === false) {
                    return false;
                }
                this.dragging = true;
                this.userModified = false;
                this.mouseMoved = false;

                if (this.isToggleSwitch) {
                    this.beforeStart = this.element[0].selectedIndex;
                }

                this.refresh(event);
                this._trigger("start");
                return false;
            },

            _sliderVMouseUp: function () {
                if (this.dragging) {
                    this.dragging = false;

                    if (this.isToggleSwitch) {
                        // make the handle move with a smooth transition
                        this.handle.addClass("ui-slider-handle-snapping");

                        if (this.mouseMoved) {
                            // this is a drag, change the value only if user dragged enough
                            if (this.userModified) {
                                this.refresh(this.beforeStart === 0 ? 1 : 0);
                            } else {
                                this.refresh(this.beforeStart);
                            }
                        } else {
                            // this is just a click, change the value
                            this.refresh(this.beforeStart === 0 ? 1 : 0);
                        }
                    }

                    this.mouseMoved = false;
                    this._trigger("stop");
                    return false;
                }
            },

            _preventDocumentDrag: function (event) {
                // NOTE: we don't do this in refresh because we still want to
                //       support programmatic alteration of disabled inputs
                if (this._trigger("drag", event) === false) {
                    return false;
                }
                if (this.dragging && !this.options.disabled) {

                    // this.mouseMoved must be updated before refresh() because it will be used in the control "change" event
                    this.mouseMoved = true;

                    if (this.isToggleSwitch) {
                        // make the handle move in sync with the mouse
                        this.handle.removeClass("ui-slider-handle-snapping");
                    }

                    this.refresh(event);

                    // only after refresh() you can calculate this.userModified
                    this.userModified = this.beforeStart !== this.element[0].selectedIndex;
                    return false;
                }
            },

            _checkedRefresh: function () {
                if (this.value !== this._value()) {
                    this.refresh(this._value());
                }
            },

            _value: function () {
                return this.isToggleSwitch ? this.element[0].selectedIndex : parseFloat(this.element.val());
            },

            _reset: function () {
                this.refresh(undefined, false, true);
            },

            refresh: function (val, isfromControl, preventInputUpdate) {
                // NOTE: we don't return here because we want to support programmatic
                //       alteration of the input value, which should still update the slider

                var self = this,
                    parentTheme = $.mobile.getAttribute(this.element[0], "theme"),
                    theme = this.options.theme || parentTheme,
                    themeClass = theme ? " ui-btn-" + theme : "",
                    trackTheme = this.options.trackTheme || parentTheme,
                    trackThemeClass = trackTheme ? " ui-bar-" + trackTheme : " ui-bar-inherit",
                    cornerClass = this.options.corners ? " ui-corner-all" : "",
                    miniClass = this.options.mini ? " ui-mini" : "",
                    left, width, data, tol,
                    pxStep, percent,
                    control, isInput, optionElements, min, max, step,
                    newval, valModStep, alignValue, percentPerStep,
                    handlePercent, aPercent, bPercent,
                    valueChanged;

                self.slider[0].className = [this.isToggleSwitch ? "ui-slider ui-slider-switch ui-slider-track ui-shadow-inset" : "ui-slider-track ui-shadow-inset", trackThemeClass, cornerClass, miniClass].join("");
                if (this.options.disabled || this.element.prop("disabled")) {
                    this.disable();
                }

                // set the stored value for comparison later
                this.value = this._value();
                if (this.options.highlight && !this.isToggleSwitch && this.slider.find(".ui-slider-bg").length === 0) {
                    this.valuebg = (function () {
                        var bg = document.createElement("div");
                        bg.className = "ui-slider-bg " + $.mobile.activeBtnClass;
                        return $(bg).prependTo(self.slider);
                    })();
                }
                this.handle.addClass("ui-btn" + themeClass + " ui-shadow");

                control = this.element;
                isInput = !this.isToggleSwitch;
                optionElements = isInput ? [] : control.find("option");
                min = isInput ? parseFloat(control.attr("min")) : 0;
                max = isInput ? parseFloat(control.attr("max")) : optionElements.length - 1;
                step = (isInput && parseFloat(control.attr("step")) > 0) ? parseFloat(control.attr("step")) : 1;

                if (typeof val === "object") {
                    data = val;
                    // a slight tolerance helped get to the ends of the slider
                    tol = 8;

                    left = this.slider.offset().left;
                    width = this.slider.width();
                    pxStep = width / ((max - min) / step);
                    if (!this.dragging ||
                            data.pageX < left - tol ||
                            data.pageX > left + width + tol) {
                        return;
                    }
                    if (pxStep > 1) {
                        percent = ((data.pageX - left) / width) * 100;
                    } else {
                        percent = Math.round(((data.pageX - left) / width) * 100);
                    }
                } else {
                    if (val == null) {
                        val = isInput ? parseFloat(control.val() || 0) : control[0].selectedIndex;
                    }
                    percent = (parseFloat(val) - min) / (max - min) * 100;
                }

                if (isNaN(percent)) {
                    return;
                }

                newval = (percent / 100) * (max - min) + min;

                //from jQuery UI slider, the following source will round to the nearest step
                valModStep = (newval - min) % step;
                alignValue = newval - valModStep;

                if (Math.abs(valModStep) * 2 >= step) {
                    alignValue += (valModStep > 0) ? step : (-step);
                }

                percentPerStep = 100 / ((max - min) / step);
                // Since JavaScript has problems with large floats, round
                // the final value to 5 digits after the decimal point (see jQueryUI: #4124)
                newval = parseFloat(alignValue.toFixed(5));

                if (typeof pxStep === "undefined") {
                    pxStep = width / ((max - min) / step);
                }
                if (pxStep > 1 && isInput) {
                    percent = (newval - min) * percentPerStep * (1 / step);
                }
                if (percent < 0) {
                    percent = 0;
                }

                if (percent > 100) {
                    percent = 100;
                }

                if (newval < min) {
                    newval = min;
                }

                if (newval > max) {
                    newval = max;
                }

                this.handle.css("left", percent + "%");

                this.handle[0].setAttribute("aria-valuenow", isInput ? newval : optionElements.eq(newval).attr("value"));

                this.handle[0].setAttribute("aria-valuetext", isInput ? newval : optionElements.eq(newval).getEncodedText());

                this.handle[0].setAttribute("title", isInput ? newval : optionElements.eq(newval).getEncodedText());

                if (this.valuebg) {
                    this.valuebg.css("width", percent + "%");
                }

                // drag the label widths
                if (this._labels) {
                    handlePercent = this.handle.width() / this.slider.width() * 100;
                    aPercent = percent && handlePercent + (100 - handlePercent) * percent / 100;
                    bPercent = percent === 100 ? 0 : Math.min(handlePercent + 100 - aPercent, 100);

                    this._labels.each(function () {
                        var ab = $(this).hasClass("ui-slider-label-a");
                        $(this).width((ab ? aPercent : bPercent) + "%");
                    });
                }

                if (!preventInputUpdate) {
                    valueChanged = false;

                    // update control"s value
                    if (isInput) {
                        valueChanged = parseFloat(control.val()) !== newval;
                        control.val(newval);
                    } else {
                        valueChanged = control[0].selectedIndex !== newval;
                        control[0].selectedIndex = newval;
                    }
                    if (this._trigger("beforechange", val) === false) {
                        return false;
                    }
                    if (!isfromControl && valueChanged) {
                        control.trigger("change");
                    }
                }
            },

            _setHighlight: function (value) {
                value = !!value;
                if (value) {
                    this.options.highlight = !!value;
                    this.refresh();
                } else if (this.valuebg) {
                    this.valuebg.remove();
                    this.valuebg = false;
                }
            },

            _setTheme: function (value) {
                this.handle
                    .removeClass("ui-btn-" + this.options.theme)
                    .addClass("ui-btn-" + value);

                var currentTheme = this.options.theme ? this.options.theme : "inherit",
                    newTheme = value ? value : "inherit";

                this.control
                    .removeClass("ui-body-" + currentTheme)
                    .addClass("ui-body-" + newTheme);
            },

            _setTrackTheme: function (value) {
                var currentTrackTheme = this.options.trackTheme ? this.options.trackTheme : "inherit",
                    newTrackTheme = value ? value : "inherit";

                this.slider
                    .removeClass("ui-body-" + currentTrackTheme)
                    .addClass("ui-body-" + newTrackTheme);
            },

            _setMini: function (value) {
                value = !!value;
                if (!this.isToggleSwitch && !this.isRangeslider) {
                    this.slider.parent().toggleClass("ui-mini", value);
                    this.element.toggleClass("ui-mini", value);
                }
                this.slider.toggleClass("ui-mini", value);
            },

            _setCorners: function (value) {
                this.slider.toggleClass("ui-corner-all", value);

                if (!this.isToggleSwitch) {
                    this.control.toggleClass("ui-corner-all", value);
                }
            },

            _setDisabled: function (value) {
                value = !!value;
                this.element.prop("disabled", value);
                this.slider
                    .toggleClass("ui-state-disabled", value)
                    .attr("aria-disabled", value);

                this.element.toggleClass("ui-state-disabled", value);
            }

        }, $.mobile.behaviors.formReset));

    })(jQuery);

    (function ($, undefined) {
        $.widget("mobile.rangeslider", $.extend({

            options: {
                theme: null,
                trackTheme: null,
                corners: true,
                mini: false,
                highlight: true
            },

            _create: function () {
                var $el = this.element,
                elClass = this.options.mini ? "ui-rangeslider ui-mini" : "ui-rangeslider",
                _inputFirst = $el.find("input").first(),
                _inputLast = $el.find("input").last(),
                _label = $el.find("label").first(),
                _sliderWidgetFirst = $.data(_inputFirst.get(0), "mobile-slider") ||
                    $.data(_inputFirst.slider().get(0), "mobile-slider"),
                _sliderWidgetLast = $.data(_inputLast.get(0), "mobile-slider") ||
                    $.data(_inputLast.slider().get(0), "mobile-slider"),
                _sliderFirst = _sliderWidgetFirst.slider,
                _sliderLast = _sliderWidgetLast.slider,
                firstHandle = _sliderWidgetFirst.handle,
                _sliders = $("<div class='ui-rangeslider-sliders' />").appendTo($el);

                _inputFirst.addClass("ui-rangeslider-first");
                _inputLast.addClass("ui-rangeslider-last");
                $el.addClass(elClass);

                _sliderFirst.appendTo(_sliders);
                _sliderLast.appendTo(_sliders);
                _label.insertBefore($el);
                firstHandle.prependTo(_sliderLast);

                $.extend(this, {
                    _inputFirst: _inputFirst,
                    _inputLast: _inputLast,
                    _sliderFirst: _sliderFirst,
                    _sliderLast: _sliderLast,
                    _label: _label,
                    _targetVal: null,
                    _sliderTarget: false,
                    _sliders: _sliders,
                    _proxy: false
                });

                this.refresh();
                this._on(this.element.find("input.ui-slider-input"), {
                    "slidebeforestart": "_slidebeforestart",
                    "slidestop": "_slidestop",
                    "slidedrag": "_slidedrag",
                    "slidebeforechange": "_change",
                    "blur": "_change",
                    "keyup": "_change"
                });
                this._on({
                    "mousedown": "_change"
                });
                this._on(this.element.closest("form"), {
                    "reset": "_handleReset"
                });
                this._on(firstHandle, {
                    "vmousedown": "_dragFirstHandle"
                });
            },
            _handleReset: function () {
                var self = this;
                //we must wait for the stack to unwind before updateing other wise sliders will not have updated yet
                setTimeout(function () {
                    self._updateHighlight();
                }, 0);
            },

            _dragFirstHandle: function (event) {
                //if the first handle is dragged send the event to the first slider
                $.data(this._inputFirst.get(0), "mobile-slider").dragging = true;
                $.data(this._inputFirst.get(0), "mobile-slider").refresh(event);
                $.data(this._inputFirst.get(0), "mobile-slider")._trigger("start");
                return false;
            },

            _slidedrag: function (event) {
                var first = $(event.target).is(this._inputFirst),
                    otherSlider = (first) ? this._inputLast : this._inputFirst;

                this._sliderTarget = false;
                //if the drag was initiated on an extreme and the other handle is focused send the events to
                //the closest handle
                if ((this._proxy === "first" && first) || (this._proxy === "last" && !first)) {
                    $.data(otherSlider.get(0), "mobile-slider").dragging = true;
                    $.data(otherSlider.get(0), "mobile-slider").refresh(event);
                    return false;
                }
            },

            _slidestop: function (event) {
                var first = $(event.target).is(this._inputFirst);

                this._proxy = false;
                //this stops dragging of the handle and brings the active track to the front
                //this makes clicks on the track go the the last handle used
                this.element.find("input").trigger("vmouseup");
                this._sliderFirst.css("z-index", first ? 1 : "");
            },

            _slidebeforestart: function (event) {
                this._sliderTarget = false;
                //if the track is the target remember this and the original value
                if ($(event.originalEvent.target).hasClass("ui-slider-track")) {
                    this._sliderTarget = true;
                    this._targetVal = $(event.target).val();
                }
            },

            _setOptions: function (options) {
                if (options.theme !== undefined) {
                    this._setTheme(options.theme);
                }

                if (options.trackTheme !== undefined) {
                    this._setTrackTheme(options.trackTheme);
                }

                if (options.mini !== undefined) {
                    this._setMini(options.mini);
                }

                if (options.highlight !== undefined) {
                    this._setHighlight(options.highlight);
                }

                if (options.disabled !== undefined) {
                    this._setDisabled(options.disabled);
                }

                this._super(options);
                this.refresh();
            },

            refresh: function () {
                var $el = this.element,
                    o = this.options;

                if (this._inputFirst.is(":disabled") || this._inputLast.is(":disabled")) {
                    this.options.disabled = true;
                }

                $el.find("input").slider({
                    theme: o.theme,
                    trackTheme: o.trackTheme,
                    disabled: o.disabled,
                    corners: o.corners,
                    mini: o.mini,
                    highlight: o.highlight
                }).slider("refresh");
                this._updateHighlight();
            },

            _change: function (event) {
                if (event.type === "keyup") {
                    this._updateHighlight();
                    return false;
                }

                var self = this,
                    min = parseFloat(this._inputFirst.val(), 10),
                    max = parseFloat(this._inputLast.val(), 10),
                    first = $(event.target).hasClass("ui-rangeslider-first"),
                    thisSlider = first ? this._inputFirst : this._inputLast,
                    otherSlider = first ? this._inputLast : this._inputFirst;

                if ((this._inputFirst.val() > this._inputLast.val() && event.type === "mousedown" && !$(event.target).hasClass("ui-slider-handle"))) {
                    thisSlider.blur();
                } else if (event.type === "mousedown") {
                    return;
                }
                if (min > max && !this._sliderTarget) {
                    //this prevents min from being greater then max
                    thisSlider.val(first ? max : min).slider("refresh");
                    this._trigger("normalize");
                } else if (min > max) {
                    //this makes it so clicks on the target on either extreme go to the closest handle
                    thisSlider.val(this._targetVal).slider("refresh");

                    //You must wait for the stack to unwind so first slider is updated before updating second
                    setTimeout(function () {
                        otherSlider.val(first ? min : max).slider("refresh");
                        $.data(otherSlider.get(0), "mobile-slider").handle.focus();
                        self._sliderFirst.css("z-index", first ? "" : 1);
                        self._trigger("normalize");
                    }, 0);
                    this._proxy = (first) ? "first" : "last";
                }
                //fixes issue where when both _sliders are at min they cannot be adjusted
                if (min === max) {
                    $.data(thisSlider.get(0), "mobile-slider").handle.css("z-index", 1);
                    $.data(otherSlider.get(0), "mobile-slider").handle.css("z-index", 0);
                } else {
                    $.data(otherSlider.get(0), "mobile-slider").handle.css("z-index", "");
                    $.data(thisSlider.get(0), "mobile-slider").handle.css("z-index", "");
                }

                this._updateHighlight();

                if (min >= max) {
                    return false;
                }
            },

            _updateHighlight: function () {
                var min = parseInt($.data(this._inputFirst.get(0), "mobile-slider").handle.get(0).style.left, 10),
                    max = parseInt($.data(this._inputLast.get(0), "mobile-slider").handle.get(0).style.left, 10),
                    width = (max - min);

                this.element.find(".ui-slider-bg").css({
                    "margin-left": min + "%",
                    "width": width + "%"
                });
            },

            _setTheme: function (value) {
                this._inputFirst.slider("option", "theme", value);
                this._inputLast.slider("option", "theme", value);
            },

            _setTrackTheme: function (value) {
                this._inputFirst.slider("option", "trackTheme", value);
                this._inputLast.slider("option", "trackTheme", value);
            },

            _setMini: function (value) {
                this._inputFirst.slider("option", "mini", value);
                this._inputLast.slider("option", "mini", value);
                this.element.toggleClass("ui-mini", !!value);
            },

            _setHighlight: function (value) {
                this._inputFirst.slider("option", "highlight", value);
                this._inputLast.slider("option", "highlight", value);
            },

            _setDisabled: function (value) {
                this._inputFirst.prop("disabled", value);
                this._inputLast.prop("disabled", value);
            },

            _destroy: function () {
                this._label.prependTo(this.element);
                this.element.removeClass("ui-rangeslider ui-mini");
                this._inputFirst.after(this._sliderFirst);
                this._inputLast.after(this._sliderLast);
                this._sliders.remove();
                this.element.find("input").removeClass("ui-rangeslider-first ui-rangeslider-last").slider("destroy");
            }

        }, $.mobile.behaviors.formReset));

    })(jQuery);

    (function ($, undefined) {

        $.widget("mobile.selectmenu", $.extend({
            initSelector: "select:not( :jqmData(role='slider')):not( :jqmData(role='flipswitch') )",

            options: {
                theme: null,
                icon: "carat-d",
                iconpos: "right",
                inline: false,
                corners: true,
                shadow: true,
                iconshadow: false, /* TODO: Deprecated in 1.4, remove in 1.5. */
                overlayTheme: null,
                dividerTheme: null,
                hidePlaceholderMenuItems: true,
                closeText: "Close",
                nativeMenu: true,
                // This option defaults to true on iOS devices.
                preventFocusZoom: /iPhone|iPad|iPod/.test(navigator.platform) && navigator.userAgent.indexOf("AppleWebKit") > -1,
                mini: false
            },

            _button: function () {
                return $("<div/>");
            },

            _setDisabled: function (value) {
                this.element.attr("disabled", value);
                this.button.attr("aria-disabled", value);
                return this._setOption("disabled", value);
            },

            _focusButton: function () {
                var self = this;

                setTimeout(function () {
                    self.button.focus();
                }, 40);
            },

            _selectOptions: function () {
                return this.select.find("option");
            },

            // setup items that are generally necessary for select menu extension
            _preExtension: function () {
                var inline = this.options.inline || this.element.jqmData("inline"),
                    mini = this.options.mini || this.element.jqmData("mini"),
                    classes = "";
                // TODO: Post 1.1--once we have time to test thoroughly--any classes manually applied to the original element should be carried over to the enhanced element, with an `-enhanced` suffix. See https://github.com/jquery/jquery-mobile/issues/3577
                /* if ( $el[0].className.length ) {
                    classes = $el[0].className;
                } */
                if (!!~this.element[0].className.indexOf("ui-btn-left")) {
                    classes = " ui-btn-left";
                }

                if (!!~this.element[0].className.indexOf("ui-btn-right")) {
                    classes = " ui-btn-right";
                }

                if (inline) {
                    classes += " ui-btn-inline";
                }
                if (mini) {
                    classes += " ui-mini";
                }

                this.select = this.element.removeClass("ui-btn-left ui-btn-right").wrap("<div class='ui-select" + classes + "'>");
                this.selectId = this.select.attr("id") || ("select-" + this.uuid);
                this.buttonId = this.selectId + "-button";
                this.label = $("label[for='" + this.selectId + "']");
                this.isMultiple = this.select[0].multiple;
            },

            _destroy: function () {
                var wrapper = this.element.parents(".ui-select");
                if (wrapper.length > 0) {
                    if (wrapper.is(".ui-btn-left, .ui-btn-right")) {
                        this.element.addClass(wrapper.hasClass("ui-btn-left") ? "ui-btn-left" : "ui-btn-right");
                    }
                    this.element.insertAfter(wrapper);
                    wrapper.remove();
                }
            },

            _create: function () {
                this._preExtension();

                this.button = this._button();

                var self = this,

                    options = this.options,

                    iconpos = options.icon ? (options.iconpos || this.select.jqmData("iconpos")) : false,

                    button = this.button
                        .insertBefore(this.select)
                        .attr("id", this.buttonId)
                        .addClass("ui-btn" +
                            (options.icon ? (" ui-icon-" + options.icon + " ui-btn-icon-" + iconpos +
                            (options.iconshadow ? " ui-shadow-icon" : "")) : "") + /* TODO: Remove in 1.5. */
                            (options.theme ? " ui-btn-" + options.theme : "") +
                            (options.corners ? " ui-corner-all" : "") +
                            (options.shadow ? " ui-shadow" : ""));

                this.setButtonText();

                // Opera does not properly support opacity on select elements
                // In Mini, it hides the element, but not its text
                // On the desktop,it seems to do the opposite
                // for these reasons, using the nativeMenu option results in a full native select in Opera
                if (options.nativeMenu && window.opera && window.opera.version) {
                    button.addClass("ui-select-nativeonly");
                }

                // Add counter for multi selects
                if (this.isMultiple) {
                    this.buttonCount = $("<span>")
                        .addClass("ui-li-count ui-body-inherit")
                        .hide()
                        .appendTo(button.addClass("ui-li-has-count"));
                }

                // Disable if specified
                if (options.disabled || this.element.attr("disabled")) {
                    this.disable();
                }

                // Events on native select
                this.select.change(function () {
                    self.refresh();

                    if (!!options.nativeMenu) {
                        self._delay(function () {
                            self.select.blur();
                        });
                    }
                });

                this._handleFormReset();

                this._on(this.button, {
                    keydown: "_handleKeydown"
                });

                this.build();
            },

            build: function () {
                var self = this;

                this.select
                    .appendTo(self.button)
                    .bind("vmousedown", function () {
                        // Add active class to button
                        self.button.addClass($.mobile.activeBtnClass);
                    })
                    .bind("focus", function () {
                        self.button.addClass($.mobile.focusClass);
                    })
                    .bind("blur", function () {
                        self.button.removeClass($.mobile.focusClass);
                    })
                    .bind("focus vmouseover", function () {
                        self.button.trigger("vmouseover");
                    })
                    .bind("vmousemove", function () {
                        // Remove active class on scroll/touchmove
                        self.button.removeClass($.mobile.activeBtnClass);
                    })
                    .bind("change blur vmouseout", function () {
                        self.button.trigger("vmouseout")
                            .removeClass($.mobile.activeBtnClass);
                    });

                // In many situations, iOS will zoom into the select upon tap, this prevents that from happening
                self.button.bind("vmousedown", function () {
                    if (self.options.preventFocusZoom) {
                        $.mobile.zoom.disable(true);
                    }
                });
                self.label.bind("click focus", function () {
                    if (self.options.preventFocusZoom) {
                        $.mobile.zoom.disable(true);
                    }
                });
                self.select.bind("focus", function () {
                    if (self.options.preventFocusZoom) {
                        $.mobile.zoom.disable(true);
                    }
                });
                self.button.bind("mouseup", function () {
                    if (self.options.preventFocusZoom) {
                        setTimeout(function () {
                            $.mobile.zoom.enable(true);
                        }, 0);
                    }
                });
                self.select.bind("blur", function () {
                    if (self.options.preventFocusZoom) {
                        $.mobile.zoom.enable(true);
                    }
                });

            },

            selected: function () {
                return this._selectOptions().filter(":selected");
            },

            selectedIndices: function () {
                var self = this;

                return this.selected().map(function () {
                    return self._selectOptions().index(this);
                }).get();
            },

            setButtonText: function () {
                var self = this,
                    selected = this.selected(),
                    text = this.placeholder,
                    span = $(document.createElement("span"));

                this.button.children("span").not(".ui-li-count").remove().end().end().prepend((function () {
                    if (selected.length) {
                        text = selected.map(function () {
                            return $(this).text();
                        }).get().join(", ");
                    } else {
                        text = self.placeholder;
                    }

                    if (text) {
                        span.text(text);
                    } else {

                        // Set the contents to &nbsp; which we write as &#160; to be XHTML compliant - see gh-6699
                        span.html("&#160;");
                    }

                    // TODO possibly aggregate multiple select option classes
                    return span
                        .addClass(self.select.attr("class"))
                        .addClass(selected.attr("class"))
                        .removeClass("ui-screen-hidden");
                })());
            },

            setButtonCount: function () {
                var selected = this.selected();

                // multiple count inside button
                if (this.isMultiple) {
                    this.buttonCount[selected.length > 1 ? "show" : "hide"]().text(selected.length);
                }
            },

            _handleKeydown: function ( /* event */) {
                this._delay("_refreshButton");
            },

            _reset: function () {
                this.refresh();
            },

            _refreshButton: function () {
                this.setButtonText();
                this.setButtonCount();
            },

            refresh: function () {
                this._refreshButton();
            },

            // open and close preserved in native selects
            // to simplify users code when looping over selects
            open: $.noop,
            close: $.noop,

            disable: function () {
                this._setDisabled(true);
                this.button.addClass("ui-state-disabled");
            },

            enable: function () {
                this._setDisabled(false);
                this.button.removeClass("ui-state-disabled");
            }
        }, $.mobile.behaviors.formReset));

    })(jQuery);

    (function ($, undefined) {

        function fitSegmentInsideSegment(windowSize, segmentSize, offset, desired) {
            var returnValue = desired;

            if (windowSize < segmentSize) {
                // Center segment if it's bigger than the window
                returnValue = offset + (windowSize - segmentSize) / 2;
            } else {
                // Otherwise center it at the desired coordinate while keeping it completely inside the window
                returnValue = Math.min(Math.max(offset, desired - segmentSize / 2), offset + windowSize - segmentSize);
            }

            return returnValue;
        }

        function getWindowCoordinates(theWindow) {
            return {
                x: theWindow.scrollLeft(),
                y: theWindow.scrollTop(),
                cx: (theWindow[0].innerWidth || theWindow.width()),
                cy: (theWindow[0].innerHeight || theWindow.height())
            };
        }

        $.widget("mobile.popup", {
            options: {
                wrapperClass: null,
                theme: null,
                overlayTheme: null,
                shadow: true,
                corners: true,
                transition: "none",
                positionTo: "origin",
                tolerance: null,
                closeLinkSelector: "a:jqmData(rel='back')",
                closeLinkEvents: "click.popup",
                navigateEvents: "navigate.popup",
                closeEvents: "navigate.popup pagebeforechange.popup",
                dismissible: true,
                enhanced: false,

                // NOTE Windows Phone 7 has a scroll position caching issue that
                //      requires us to disable popup history management by default
                //      https://github.com/jquery/jquery-mobile/issues/4784
                //
                // NOTE this option is modified in _create!
                history: !$.mobile.browser.oldIE
            },

            // When the user depresses the mouse/finger on an element inside the popup while the popup is
            // open, we ignore resize events for a short while. This prevents #6961.
            _handleDocumentVmousedown: function (theEvent) {
                if (this._isOpen && $.contains(this._ui.container[0], theEvent.target)) {
                    this._ignoreResizeEvents();
                }
            },

            _create: function () {
                var theElement = this.element,
                    myId = theElement.attr("id"),
                    currentOptions = this.options;

                // We need to adjust the history option to be false if there's no AJAX nav.
                // We can't do it in the option declarations because those are run before
                // it is determined whether there shall be AJAX nav.
                currentOptions.history = currentOptions.history && $.mobile.ajaxEnabled && $.mobile.hashListeningEnabled;

                this._on(this.document, {
                    "vmousedown": "_handleDocumentVmousedown"
                });

                // Define instance variables
                $.extend(this, {
                    _scrollTop: 0,
                    _page: theElement.closest(".ui-page"),
                    _ui: null,
                    _fallbackTransition: "",
                    _currentTransition: false,
                    _prerequisites: null,
                    _isOpen: false,
                    _tolerance: null,
                    _resizeData: null,
                    _ignoreResizeTo: 0,
                    _orientationchangeInProgress: false
                });

                if (this._page.length === 0) {
                    this._page = $("body");
                }

                if (currentOptions.enhanced) {
                    this._ui = {
                        container: theElement.parent(),
                        screen: theElement.parent().prev(),
                        placeholder: $(this.document[0].getElementById(myId + "-placeholder"))
                    };
                } else {
                    this._ui = this._enhance(theElement, myId);
                    this._applyTransition(currentOptions.transition);
                }
                this
                    ._setTolerance(currentOptions.tolerance)
                    ._ui.focusElement = this._ui.container;

                // Event handlers
                this._on(this._ui.screen, { "vclick": "_eatEventAndClose" });
                this._on(this.window, {
                    orientationchange: $.proxy(this, "_handleWindowOrientationchange"),
                    resize: $.proxy(this, "_handleWindowResize"),
                    keyup: $.proxy(this, "_handleWindowKeyUp")
                });
                this._on(this.document, { "focusin": "_handleDocumentFocusIn" });
            },

            _enhance: function (theElement, myId) {
                var currentOptions = this.options,
                    wrapperClass = currentOptions.wrapperClass,
                    ui = {
                        screen: $("<div class='ui-screen-hidden ui-popup-screen " +
                        this._themeClassFromOption("ui-overlay-", currentOptions.overlayTheme) + "'></div>"),
                        placeholder: $("<div style='display: none;'><!-- placeholder --></div>"),
                        container: $("<div class='ui-popup-container ui-popup-hidden ui-popup-truncate" +
                            (wrapperClass ? (" " + wrapperClass) : "") + "'></div>")
                    },
                    fragment = this.document[0].createDocumentFragment();

                fragment.appendChild(ui.screen[0]);
                fragment.appendChild(ui.container[0]);

                if (myId) {
                    ui.screen.attr("id", myId + "-screen");
                    ui.container.attr("id", myId + "-popup");
                    ui.placeholder
                        .attr("id", myId + "-placeholder")
                        .html("<!-- placeholder for " + myId + " -->");
                }

                // Apply the proto
                this._page[0].appendChild(fragment);
                // Leave a placeholder where the element used to be
                ui.placeholder.insertAfter(theElement);
                theElement
                    .detach()
                    .addClass("ui-popup " +
                        this._themeClassFromOption("ui-body-", currentOptions.theme) + " " +
                        (currentOptions.shadow ? "ui-overlay-shadow " : "") +
                        (currentOptions.corners ? "ui-corner-all " : ""))
                    .appendTo(ui.container);

                return ui;
            },

            _eatEventAndClose: function (theEvent) {
                theEvent.preventDefault();
                theEvent.stopImmediatePropagation();
                if (this.options.dismissible) {
                    this.close();
                }
                return false;
            },

            // Make sure the screen covers the entire document - CSS is sometimes not
            // enough to accomplish this.
            _resizeScreen: function () {
                var screen = this._ui.screen,
                    popupHeight = this._ui.container.outerHeight(true),
                    screenHeight = screen.removeAttr("style").height(),

                    // Subtracting 1 here is necessary for an obscure Andrdoid 4.0 bug where
                    // the browser hangs if the screen covers the entire document :/
                    documentHeight = this.document.height() - 1;

                if (screenHeight < documentHeight) {
                    screen.height(documentHeight);
                } else if (popupHeight > screenHeight) {
                    screen.height(popupHeight);
                }
            },

            _handleWindowKeyUp: function (theEvent) {
                if (this._isOpen && theEvent.keyCode === $.mobile.keyCode.ESCAPE) {
                    return this._eatEventAndClose(theEvent);
                }
            },

            _expectResizeEvent: function () {
                var windowCoordinates = getWindowCoordinates(this.window);

                if (this._resizeData) {
                    if (windowCoordinates.x === this._resizeData.windowCoordinates.x &&
                        windowCoordinates.y === this._resizeData.windowCoordinates.y &&
                        windowCoordinates.cx === this._resizeData.windowCoordinates.cx &&
                        windowCoordinates.cy === this._resizeData.windowCoordinates.cy) {
                        // timeout not refreshed
                        return false;
                    } else {
                        // clear existing timeout - it will be refreshed below
                        clearTimeout(this._resizeData.timeoutId);
                    }
                }

                this._resizeData = {
                    timeoutId: this._delay("_resizeTimeout", 200),
                    windowCoordinates: windowCoordinates
                };

                return true;
            },

            _resizeTimeout: function () {
                if (this._isOpen) {
                    if (!this._expectResizeEvent()) {
                        if (this._ui.container.hasClass("ui-popup-hidden")) {
                            // effectively rapid-open the popup while leaving the screen intact
                            this._ui.container.removeClass("ui-popup-hidden ui-popup-truncate");
                            this.reposition({ positionTo: "window" });
                            this._ignoreResizeEvents();
                        }

                        this._resizeScreen();
                        this._resizeData = null;
                        this._orientationchangeInProgress = false;
                    }
                } else {
                    this._resizeData = null;
                    this._orientationchangeInProgress = false;
                }
            },

            _stopIgnoringResizeEvents: function () {
                this._ignoreResizeTo = 0;
            },

            _ignoreResizeEvents: function () {
                if (this._ignoreResizeTo) {
                    clearTimeout(this._ignoreResizeTo);
                }
                this._ignoreResizeTo = this._delay("_stopIgnoringResizeEvents", 1000);
            },

            _handleWindowResize: function (/* theEvent */) {
                if (this._isOpen && this._ignoreResizeTo === 0) {
                    if ((this._expectResizeEvent() || this._orientationchangeInProgress) &&
                        !this._ui.container.hasClass("ui-popup-hidden")) {
                        // effectively rapid-close the popup while leaving the screen intact
                        this._ui.container
                            .addClass("ui-popup-hidden ui-popup-truncate")
                            .removeAttr("style");
                    }
                }
            },

            _handleWindowOrientationchange: function (/* theEvent */) {
                if (!this._orientationchangeInProgress && this._isOpen && this._ignoreResizeTo === 0) {
                    this._expectResizeEvent();
                    this._orientationchangeInProgress = true;
                }
            },

            // When the popup is open, attempting to focus on an element that is not a
            // child of the popup will redirect focus to the popup
            _handleDocumentFocusIn: function (theEvent) {
                var target,
                    targetElement = theEvent.target,
                    ui = this._ui;

                if (!this._isOpen) {
                    return;
                }

                if (targetElement !== ui.container[0]) {
                    target = $(targetElement);
                    if (!$.contains(ui.container[0], targetElement)) {
                        $(this.document[0].activeElement).one("focus", $.proxy(function () {
                            this._safelyBlur(targetElement);
                        }, this));
                        ui.focusElement.focus();
                        theEvent.preventDefault();
                        theEvent.stopImmediatePropagation();
                        return false;
                    } else if (ui.focusElement[0] === ui.container[0]) {
                        ui.focusElement = target;
                    }
                }

                this._ignoreResizeEvents();
            },

            _themeClassFromOption: function (prefix, value) {
                return (value ? (value === "none" ? "" : (prefix + value)) : (prefix + "inherit"));
            },

            _applyTransition: function (value) {
                if (value) {
                    this._ui.container.removeClass(this._fallbackTransition);
                    if (value !== "none") {
                        this._fallbackTransition = $.mobile._maybeDegradeTransition(value);
                        if (this._fallbackTransition === "none") {
                            this._fallbackTransition = "";
                        }
                        this._ui.container.addClass(this._fallbackTransition);
                    }
                }

                return this;
            },

            _setOptions: function (newOptions) {
                var currentOptions = this.options,
                    theElement = this.element,
                    screen = this._ui.screen;

                if (newOptions.wrapperClass !== undefined) {
                    this._ui.container
                        .removeClass(currentOptions.wrapperClass)
                        .addClass(newOptions.wrapperClass);
                }

                if (newOptions.theme !== undefined) {
                    theElement
                        .removeClass(this._themeClassFromOption("ui-body-", currentOptions.theme))
                        .addClass(this._themeClassFromOption("ui-body-", newOptions.theme));
                }

                if (newOptions.overlayTheme !== undefined) {
                    screen
                        .removeClass(this._themeClassFromOption("ui-overlay-", currentOptions.overlayTheme))
                        .addClass(this._themeClassFromOption("ui-overlay-", newOptions.overlayTheme));

                    if (this._isOpen) {
                        screen.addClass("in");
                    }
                }

                if (newOptions.shadow !== undefined) {
                    theElement.toggleClass("ui-overlay-shadow", newOptions.shadow);
                }

                if (newOptions.corners !== undefined) {
                    theElement.toggleClass("ui-corner-all", newOptions.corners);
                }

                if (newOptions.transition !== undefined) {
                    if (!this._currentTransition) {
                        this._applyTransition(newOptions.transition);
                    }
                }

                if (newOptions.tolerance !== undefined) {
                    this._setTolerance(newOptions.tolerance);
                }

                if (newOptions.disabled !== undefined) {
                    if (newOptions.disabled) {
                        this.close();
                    }
                }

                return this._super(newOptions);
            },

            _setTolerance: function (value) {
                var tol = { t: 30, r: 15, b: 30, l: 15 },
                    ar;

                if (value !== undefined) {
                    ar = String(value).split(",");

                    $.each(ar, function (idx, val) { ar[idx] = parseInt(val, 10); });

                    switch (ar.length) {
                        // All values are to be the same
                        case 1:
                            if (!isNaN(ar[0])) {
                                tol.t = tol.r = tol.b = tol.l = ar[0];
                            }
                            break;

                            // The first value denotes top/bottom tolerance, and the second value denotes left/right tolerance
                        case 2:
                            if (!isNaN(ar[0])) {
                                tol.t = tol.b = ar[0];
                            }
                            if (!isNaN(ar[1])) {
                                tol.l = tol.r = ar[1];
                            }
                            break;

                            // The array contains values in the order top, right, bottom, left
                        case 4:
                            if (!isNaN(ar[0])) {
                                tol.t = ar[0];
                            }
                            if (!isNaN(ar[1])) {
                                tol.r = ar[1];
                            }
                            if (!isNaN(ar[2])) {
                                tol.b = ar[2];
                            }
                            if (!isNaN(ar[3])) {
                                tol.l = ar[3];
                            }
                            break;

                        default:
                            break;
                    }
                }

                this._tolerance = tol;
                return this;
            },

            _clampPopupWidth: function (infoOnly) {
                var menuSize,
                    windowCoordinates = getWindowCoordinates(this.window),
                    // rectangle within which the popup must fit
                    rectangle = {
                        x: this._tolerance.l,
                        y: windowCoordinates.y + this._tolerance.t,
                        cx: windowCoordinates.cx - this._tolerance.l - this._tolerance.r,
                        cy: windowCoordinates.cy - this._tolerance.t - this._tolerance.b
                    };

                if (!infoOnly) {
                    // Clamp the width of the menu before grabbing its size
                    this._ui.container.css("max-width", rectangle.cx);
                }

                menuSize = {
                    cx: this._ui.container.outerWidth(true),
                    cy: this._ui.container.outerHeight(true)
                };

                return { rc: rectangle, menuSize: menuSize };
            },

            _calculateFinalLocation: function (desired, clampInfo) {
                var returnValue,
                    rectangle = clampInfo.rc,
                    menuSize = clampInfo.menuSize;

                // Center the menu over the desired coordinates, while not going outside
                // the window tolerances. This will center wrt. the window if the popup is
                // too large.
                returnValue = {
                    left: fitSegmentInsideSegment(rectangle.cx, menuSize.cx, rectangle.x, desired.x),
                    top: fitSegmentInsideSegment(rectangle.cy, menuSize.cy, rectangle.y, desired.y)
                };

                // Make sure the top of the menu is visible
                returnValue.top = Math.max(0, returnValue.top);

                // If the height of the menu is smaller than the height of the document
                // align the bottom with the bottom of the document

                returnValue.top -= Math.min(returnValue.top,
                    Math.max(0, returnValue.top + menuSize.cy - this.document.height()));

                return returnValue;
            },

            // Try and center the overlay over the given coordinates
            _placementCoords: function (desired) {
                return this._calculateFinalLocation(desired, this._clampPopupWidth());
            },

            _createPrerequisites: function (screenPrerequisite, containerPrerequisite, whenDone) {
                var prerequisites,
                    self = this;

                // It is important to maintain both the local variable prerequisites and
                // self._prerequisites. The local variable remains in the closure of the
                // functions which call the callbacks passed in. The comparison between the
                // local variable and self._prerequisites is necessary, because once a
                // function has been passed to .animationComplete() it will be called next
                // time an animation completes, even if that's not the animation whose end
                // the function was supposed to catch (for example, if an abort happens
                // during the opening animation, the .animationComplete handler is not
                // called for that animation anymore, but the handler remains attached, so
                // it is called the next time the popup is opened - making it stale.
                // Comparing the local variable prerequisites to the widget-level variable
                // self._prerequisites ensures that callbacks triggered by a stale
                // .animationComplete will be ignored.

                prerequisites = {
                    screen: $.Deferred(),
                    container: $.Deferred()
                };

                prerequisites.screen.then(function () {
                    if (prerequisites === self._prerequisites) {
                        screenPrerequisite();
                    }
                });

                prerequisites.container.then(function () {
                    if (prerequisites === self._prerequisites) {
                        containerPrerequisite();
                    }
                });

                $.when(prerequisites.screen, prerequisites.container).done(function () {
                    if (prerequisites === self._prerequisites) {
                        self._prerequisites = null;
                        whenDone();
                    }
                });

                self._prerequisites = prerequisites;
            },

            _animate: function (args) {
                // NOTE before removing the default animation of the screen
                //      this had an animate callback that would resolve the deferred
                //      now the deferred is resolved immediately
                // TODO remove the dependency on the screen deferred
                this._ui.screen
                    .removeClass(args.classToRemove)
                    .addClass(args.screenClassToAdd);

                args.prerequisites.screen.resolve();

                if (args.transition && args.transition !== "none") {
                    if (args.applyTransition) {
                        this._applyTransition(args.transition);
                    }
                    if (this._fallbackTransition) {
                        this._ui.container
                            .addClass(args.containerClassToAdd)
                            .removeClass(args.classToRemove)
                            .animationComplete($.proxy(args.prerequisites.container, "resolve"));
                        return;
                    }
                }
                this._ui.container.removeClass(args.classToRemove);
                args.prerequisites.container.resolve();
            },

            // The desired coordinates passed in will be returned untouched if no reference element can be identified via
            // desiredPosition.positionTo. Nevertheless, this function ensures that its return value always contains valid
            // x and y coordinates by specifying the center middle of the window if the coordinates are absent.
            // options: { x: coordinate, y: coordinate, positionTo: string: "origin", "window", or jQuery selector
            _desiredCoords: function (openOptions) {
                var offset,
                    dst = null,
                    windowCoordinates = getWindowCoordinates(this.window),
                    x = openOptions.x,
                    y = openOptions.y,
                    pTo = openOptions.positionTo;

                // Establish which element will serve as the reference
                if (pTo && pTo !== "origin") {
                    if (pTo === "window") {
                        x = windowCoordinates.cx / 2 + windowCoordinates.x;
                        y = windowCoordinates.cy / 2 + windowCoordinates.y;
                    } else {
                        try {
                            dst = $(pTo);
                        } catch (err) {
                            dst = null;
                        }
                        if (dst) {
                            dst.filter(":visible");
                            if (dst.length === 0) {
                                dst = null;
                            }
                        }
                    }
                }

                // If an element was found, center over it
                if (dst) {
                    offset = dst.offset();
                    x = offset.left + dst.outerWidth() / 2;
                    y = offset.top + dst.outerHeight() / 2;
                }

                // Make sure x and y are valid numbers - center over the window
                if ($.type(x) !== "number" || isNaN(x)) {
                    x = windowCoordinates.cx / 2 + windowCoordinates.x;
                }
                if ($.type(y) !== "number" || isNaN(y)) {
                    y = windowCoordinates.cy / 2 + windowCoordinates.y;
                }

                return { x: x, y: y };
            },

            _reposition: function (openOptions) {
                // We only care about position-related parameters for repositioning
                openOptions = {
                    x: openOptions.x,
                    y: openOptions.y,
                    positionTo: openOptions.positionTo
                };
                this._trigger("beforeposition", undefined, openOptions);
                this._ui.container.offset(this._placementCoords(this._desiredCoords(openOptions)));
            },

            reposition: function (openOptions) {
                if (this._isOpen) {
                    this._reposition(openOptions);
                }
            },

            _safelyBlur: function (currentElement) {
                if (currentElement !== this.window[0] &&
                    currentElement.nodeName.toLowerCase() !== "body") {
                    $(currentElement).blur();
                }
            },

            _openPrerequisitesComplete: function () {
                var id = this.element.attr("id"),
                    firstFocus = this._ui.container.find(":focusable").first();

                this._ui.container.addClass("ui-popup-active");
                this._isOpen = true;
                this._resizeScreen();

                // Check to see if currElement is not a child of the container.  If it's not, blur
                if (!$.contains(this._ui.container[0], this.document[0].activeElement)) {
                    this._safelyBlur(this.document[0].activeElement);
                }
                if (firstFocus.length > 0) {
                    this._ui.focusElement = firstFocus;
                }
                this._ignoreResizeEvents();
                if (id) {
                    this.document.find("[aria-haspopup='true'][aria-owns='" + id + "']").attr("aria-expanded", true);
                }
                this._trigger("afteropen");
            },

            _open: function (options) {
                var openOptions = $.extend({}, this.options, options),
                    // TODO move blacklist to private method
                    androidBlacklist = (function () {
                        var ua = navigator.userAgent,
                            // Rendering engine is Webkit, and capture major version
                            wkmatch = ua.match(/AppleWebKit\/([0-9\.]+)/),
                            wkversion = !!wkmatch && wkmatch[1],
                            androidmatch = ua.match(/Android (\d+(?:\.\d+))/),
                            andversion = !!androidmatch && androidmatch[1],
                            chromematch = ua.indexOf("Chrome") > -1;

                        // Platform is Android, WebKit version is greater than 534.13 ( Android 3.2.1 ) and not Chrome.
                        if (androidmatch !== null && andversion === "4.0" && wkversion && wkversion > 534.13 && !chromematch) {
                            return true;
                        }
                        return false;
                    }());

                // Count down to triggering "popupafteropen" - we have two prerequisites:
                // 1. The popup window animation completes (container())
                // 2. The screen opacity animation completes (screen())
                this._createPrerequisites(
                    $.noop,
                    $.noop,
                    $.proxy(this, "_openPrerequisitesComplete"));

                this._currentTransition = openOptions.transition;
                this._applyTransition(openOptions.transition);

                this._ui.screen.removeClass("ui-screen-hidden");
                this._ui.container.removeClass("ui-popup-truncate");

                // Give applications a chance to modify the contents of the container before it appears
                this._reposition(openOptions);

                this._ui.container.removeClass("ui-popup-hidden");

                if (this.options.overlayTheme && androidBlacklist) {
                    /* TODO: The native browser on Android 4.0.X ("Ice Cream Sandwich") suffers from an issue where the popup overlay appears to be z-indexed above the popup itself when certain other styles exist on the same page -- namely, any element set to `position: fixed` and certain types of input. These issues are reminiscent of previously uncovered bugs in older versions of Android's native browser: https://github.com/scottjehl/Device-Bugs/issues/3
                    This fix closes the following bugs ( I use "closes" with reluctance, and stress that this issue should be revisited as soon as possible ):
                    https://github.com/jquery/jquery-mobile/issues/4816
                    https://github.com/jquery/jquery-mobile/issues/4844
                    https://github.com/jquery/jquery-mobile/issues/4874
                    */

                    // TODO sort out why this._page isn't working
                    this.element.closest(".ui-page").addClass("ui-popup-open");
                }
                this._animate({
                    additionalCondition: true,
                    transition: openOptions.transition,
                    classToRemove: "",
                    screenClassToAdd: "in",
                    containerClassToAdd: "in",
                    applyTransition: false,
                    prerequisites: this._prerequisites
                });
            },

            _closePrerequisiteScreen: function () {
                this._ui.screen
                    .removeClass("out")
                    .addClass("ui-screen-hidden");
            },

            _closePrerequisiteContainer: function () {
                this._ui.container
                    .removeClass("reverse out")
                    .addClass("ui-popup-hidden ui-popup-truncate")
                    .removeAttr("style");
            },

            _closePrerequisitesDone: function () {
                var container = this._ui.container,
                    id = this.element.attr("id");

                // remove the global mutex for popups
                $.mobile.popup.active = undefined;

                // Blur elements inside the container, including the container
                $(":focus", container[0]).add(container[0]).blur();

                if (id) {
                    this.document.find("[aria-haspopup='true'][aria-owns='" + id + "']").attr("aria-expanded", false);
                }

                // alert users that the popup is closed
                this._trigger("afterclose");
            },

            _close: function (immediate) {
                this._ui.container.removeClass("ui-popup-active");
                this._page.removeClass("ui-popup-open");

                this._isOpen = false;

                // Count down to triggering "popupafterclose" - we have two prerequisites:
                // 1. The popup window reverse animation completes (container())
                // 2. The screen opacity animation completes (screen())
                this._createPrerequisites(
                    $.proxy(this, "_closePrerequisiteScreen"),
                    $.proxy(this, "_closePrerequisiteContainer"),
                    $.proxy(this, "_closePrerequisitesDone"));

                this._animate({
                    additionalCondition: this._ui.screen.hasClass("in"),
                    transition: (immediate ? "none" : (this._currentTransition)),
                    classToRemove: "in",
                    screenClassToAdd: "out",
                    containerClassToAdd: "reverse out",
                    applyTransition: true,
                    prerequisites: this._prerequisites
                });
            },

            _unenhance: function () {
                if (this.options.enhanced) {
                    return;
                }

                // Put the element back to where the placeholder was and remove the "ui-popup" class
                this._setOptions({ theme: $.mobile.popup.prototype.options.theme });
                this.element
                    // Cannot directly insertAfter() - we need to detach() first, because
                    // insertAfter() will do nothing if the payload div was not attached
                    // to the DOM at the time the widget was created, and so the payload
                    // will remain inside the container even after we call insertAfter().
                    // If that happens and we remove the container a few lines below, we
                    // will cause an infinite recursion - #5244
                    .detach()
                    .insertAfter(this._ui.placeholder)
                    .removeClass("ui-popup ui-overlay-shadow ui-corner-all ui-body-inherit");
                this._ui.screen.remove();
                this._ui.container.remove();
                this._ui.placeholder.remove();
            },

            _destroy: function () {
                if ($.mobile.popup.active === this) {
                    this.element.one("popupafterclose", $.proxy(this, "_unenhance"));
                    this.close();
                } else {
                    this._unenhance();
                }

                return this;
            },

            _closePopup: function (theEvent, data) {
                var parsedDst, toUrl,
                    currentOptions = this.options,
                    immediate = false;

                if ((theEvent && theEvent.isDefaultPrevented()) || $.mobile.popup.active !== this) {
                    return;
                }

                // restore location on screen
                window.scrollTo(0, this._scrollTop);

                if (theEvent && theEvent.type === "pagebeforechange" && data) {
                    // Determine whether we need to rapid-close the popup, or whether we can
                    // take the time to run the closing transition
                    if (typeof data.toPage === "string") {
                        parsedDst = data.toPage;
                    } else {
                        parsedDst = data.toPage.jqmData("url");
                    }
                    parsedDst = $.mobile.path.parseUrl(parsedDst);
                    toUrl = parsedDst.pathname + parsedDst.search + parsedDst.hash;

                    if (this._myUrl !== $.mobile.path.makeUrlAbsolute(toUrl)) {
                        // Going to a different page - close immediately
                        immediate = true;
                    } else {
                        theEvent.preventDefault();
                    }
                }

                // remove nav bindings
                this.window.off(currentOptions.closeEvents);
                // unbind click handlers added when history is disabled
                this.element.undelegate(currentOptions.closeLinkSelector, currentOptions.closeLinkEvents);

                this._close(immediate);
            },

            // any navigation event after a popup is opened should close the popup
            // NOTE the pagebeforechange is bound to catch navigation events that don't
            //      alter the url (eg, dialogs from popups)
            _bindContainerClose: function () {
                this.window
                    .on(this.options.closeEvents, $.proxy(this, "_closePopup"));
            },

            widget: function () {
                return this._ui.container;
            },

            // TODO no clear deliniation of what should be here and
            // what should be in _open. Seems to be "visual" vs "history" for now
            open: function (options) {
                var url, hashkey, activePage, currentIsDialog, hasHash, urlHistory,
                    self = this,
                    currentOptions = this.options;

                // make sure open is idempotent
                if ($.mobile.popup.active || currentOptions.disabled) {
                    return this;
                }

                // set the global popup mutex
                $.mobile.popup.active = this;
                this._scrollTop = this.window.scrollTop();

                // if history alteration is disabled close on navigate events
                // and leave the url as is
                if (!(currentOptions.history)) {
                    self._open(options);
                    self._bindContainerClose();

                    // When histoy is disabled we have to grab the data-rel
                    // back link clicks so we can close the popup instead of
                    // relying on history to do it for us
                    self.element
                        .delegate(currentOptions.closeLinkSelector, currentOptions.closeLinkEvents, function (theEvent) {
                            self.close();
                            theEvent.preventDefault();
                        });

                    return this;
                }

                // cache some values for min/readability
                urlHistory = $.mobile.navigate.history;
                hashkey = $.mobile.dialogHashKey;
                activePage = $.mobile.activePage;
                currentIsDialog = (activePage ? activePage.hasClass("ui-dialog") : false);
                this._myUrl = url = urlHistory.getActive().url;
                hasHash = (url.indexOf(hashkey) > -1) && !currentIsDialog && (urlHistory.activeIndex > 0);

                if (hasHash) {
                    self._open(options);
                    self._bindContainerClose();
                    return this;
                }

                // if the current url has no dialog hash key proceed as normal
                // otherwise, if the page is a dialog simply tack on the hash key
                if (url.indexOf(hashkey) === -1 && !currentIsDialog) {
                    url = url + (url.indexOf("#") > -1 ? hashkey : "#" + hashkey);
                } else {
                    url = $.mobile.path.parseLocation().hash + hashkey;
                }

                // swallow the the initial navigation event, and bind for the next
                this.window.one("beforenavigate", function (theEvent) {
                    theEvent.preventDefault();
                    self._open(options);
                    self._bindContainerClose();
                });

                this.urlAltered = true;
                $.mobile.navigate(url, { role: "dialog" });

                return this;
            },

            close: function () {
                // make sure close is idempotent
                if ($.mobile.popup.active !== this) {
                    return this;
                }

                this._scrollTop = this.window.scrollTop();

                if (this.options.history && this.urlAltered) {
                    $.mobile.back();
                    this.urlAltered = false;
                } else {
                    // simulate the nav bindings having fired
                    this._closePopup();
                }

                return this;
            }
        });

        // TODO this can be moved inside the widget
        $.mobile.popup.handleLink = function ($link) {
            var offset,
                path = $.mobile.path,

                // NOTE make sure to get only the hash from the href because ie7 (wp7)
                //      returns the absolute href in this case ruining the element selection
                popup = $(path.hashToSelector(path.parseUrl($link.attr("href")).hash)).first();

            if (popup.length > 0 && popup.data("mobile-popup")) {
                offset = $link.offset();
                popup.popup("open", {
                    x: offset.left + $link.outerWidth() / 2,
                    y: offset.top + $link.outerHeight() / 2,
                    transition: $link.jqmData("transition"),
                    positionTo: $link.jqmData("position-to")
                });
            }

            //remove after delay
            setTimeout(function () {
                $link.removeClass($.mobile.activeBtnClass);
            }, 300);
        };

        // TODO move inside _create
        $.mobile.document.on("pagebeforechange", function (theEvent, data) {
            if (data.options.role === "popup") {
                $.mobile.popup.handleLink(data.options.link);
                theEvent.preventDefault();
            }
        });

    })(jQuery);

    /*
    * custom "selectmenu" plugin
    */

    (function ($, undefined) {

        var unfocusableItemSelector = ".ui-disabled,.ui-state-disabled,.ui-li-divider,.ui-screen-hidden,:jqmData(role='placeholder')",
            goToAdjacentItem = function (item, target, direction) {
                var adjacent = item[direction + "All"]()
                    .not(unfocusableItemSelector)
                    .first();

                // if there's a previous option, focus it
                if (adjacent.length) {
                    target
                        .blur()
                        .attr("tabindex", "-1");

                    adjacent.find("a").first().focus();
                }
            };

        $.widget("mobile.selectmenu", $.mobile.selectmenu, {
            _create: function () {
                var o = this.options;

                // Custom selects cannot exist inside popups, so revert the "nativeMenu"
                // option to true if a parent is a popup
                o.nativeMenu = o.nativeMenu || (this.element.parents(":jqmData(role='popup'),:mobile-popup").length > 0);

                return this._super();
            },

            _handleSelectFocus: function () {
                this.element.blur();
                this.button.focus();
            },

            _handleKeydown: function (event) {
                this._super(event);
                this._handleButtonVclickKeydown(event);
            },

            _handleButtonVclickKeydown: function (event) {
                if (this.options.disabled || this.isOpen || this.options.nativeMenu) {
                    return;
                }

                if (event.type === "vclick" ||
                        event.keyCode && (event.keyCode === $.mobile.keyCode.ENTER || event.keyCode === $.mobile.keyCode.SPACE)) {

                    this._decideFormat();
                    if (this.menuType === "overlay") {
                        this.button.attr("href", "#" + this.popupId).attr("data-" + ($.mobile.ns || "") + "rel", "popup");
                    } else {
                        this.button.attr("href", "#" + this.dialogId).attr("data-" + ($.mobile.ns || "") + "rel", "dialog");
                    }
                    this.isOpen = true;
                    // Do not prevent default, so the navigation may have a chance to actually open the chosen format
                }
            },

            _handleListFocus: function (e) {
                var params = (e.type === "focusin") ?
			{ tabindex: "0", event: "vmouseover" } :
			{ tabindex: "-1", event: "vmouseout" };

                $(e.target)
                    .attr("tabindex", params.tabindex)
                    .trigger(params.event);
            },

            _handleListKeydown: function (event) {
                var target = $(event.target),
                    li = target.closest("li");

                // switch logic based on which key was pressed
                switch (event.keyCode) {
                    // up or left arrow keys
                    case 38:
                        goToAdjacentItem(li, target, "prev");
                        return false;
                        // down or right arrow keys
                    case 40:
                        goToAdjacentItem(li, target, "next");
                        return false;
                        // If enter or space is pressed, trigger click
                    case 13:
                    case 32:
                        target.trigger("click");
                        return false;
                }
            },

            _handleMenuPageHide: function () {

                // After the dialog's done, we may want to trigger change if the value has actually changed
                this._delayedTrigger();

                // TODO centralize page removal binding / handling in the page plugin.
                // Suggestion from @jblas to do refcounting
                //
                // TODO extremely confusing dependency on the open method where the pagehide.remove
                // bindings are stripped to prevent the parent page from disappearing. The way
                // we're keeping pages in the DOM right now sucks
                //
                // rebind the page remove that was unbound in the open function
                // to allow for the parent page removal from actions other than the use
                // of a dialog sized custom select
                //
                // doing this here provides for the back button on the custom select dialog
                this.thisPage.page("bindRemove");
            },

            _handleHeaderCloseClick: function () {
                if (this.menuType === "overlay") {
                    this.close();
                    return false;
                }
            },

            _handleListItemClick: function (event) {
                var listItem = $(event.target).closest("li"),

                    // Index of option tag to be selected
                    oldIndex = this.select[0].selectedIndex,
                    newIndex = $.mobile.getAttribute(listItem, "option-index"),
                    option = this._selectOptions().eq(newIndex)[0];

                // Toggle selected status on the tag for multi selects
                option.selected = this.isMultiple ? !option.selected : true;

                // Toggle checkbox class for multiple selects
                if (this.isMultiple) {
                    listItem.find("a")
                        .toggleClass("ui-checkbox-on", option.selected)
                        .toggleClass("ui-checkbox-off", !option.selected);
                }

                // If it's not a multiple select, trigger change after it has finished closing
                if (!this.isMultiple && oldIndex !== newIndex) {
                    this._triggerChange = true;
                }

                // Trigger change if it's a multiple select
                // Hide custom select for single selects only - otherwise focus clicked item
                // We need to grab the clicked item the hard way, because the list may have been rebuilt
                if (this.isMultiple) {
                    this.select.trigger("change");
                    this.list.find("li:not(.ui-li-divider)").eq(newIndex)
                        .find("a").first().focus();
                }
                else {
                    this.close();
                }

                event.preventDefault();
            },

            build: function () {
                var selectId, popupId, dialogId, label, thisPage, isMultiple, menuId,
                    themeAttr, overlayTheme, overlayThemeAttr, dividerThemeAttr,
                    menuPage, listbox, list, header, headerTitle, menuPageContent,
                    menuPageClose, headerClose,
                    o = this.options;

                if (o.nativeMenu) {
                    return this._super();
                }

                selectId = this.selectId;
                popupId = selectId + "-listbox";
                dialogId = selectId + "-dialog";
                label = this.label;
                thisPage = this.element.closest(".ui-page");
                isMultiple = this.element[0].multiple;
                menuId = selectId + "-menu";
                themeAttr = o.theme ? (" data-" + $.mobile.ns + "theme='" + o.theme + "'") : "";
                overlayTheme = o.overlayTheme || o.theme || null;
                overlayThemeAttr = overlayTheme ? (" data-" + $.mobile.ns +
                    "overlay-theme='" + overlayTheme + "'") : "";
                dividerThemeAttr = (o.dividerTheme && isMultiple) ? (" data-" + $.mobile.ns + "divider-theme='" + o.dividerTheme + "'") : "";
                menuPage = $("<div data-" + $.mobile.ns + "role='dialog' class='ui-selectmenu' id='" + dialogId + "'" + themeAttr + overlayThemeAttr + ">" +
                    "<div data-" + $.mobile.ns + "role='header'>" +
                    "<div class='ui-title'></div>" +
                    "</div>" +
                    "<div data-" + $.mobile.ns + "role='content'></div>" +
                    "</div>");
                listbox = $("<div" + themeAttr + overlayThemeAttr + " id='" + popupId +
                        "' class='ui-selectmenu'></div>")
                    .insertAfter(this.select)
                    .popup();
                list = $("<ul class='ui-selectmenu-list' id='" + menuId + "' role='listbox' aria-labelledby='" + this.buttonId + "'" + themeAttr + dividerThemeAttr + "></ul>").appendTo(listbox);
                header = $("<div class='ui-header ui-bar-" + (o.theme ? o.theme : "inherit") + "'></div>").prependTo(listbox);
                headerTitle = $("<h1 class='ui-title'></h1>").appendTo(header);

                if (this.isMultiple) {
                    headerClose = $("<a>", {
                        "role": "button",
                        "text": o.closeText,
                        "href": "#",
                        "class": "ui-btn ui-corner-all ui-btn-left ui-btn-icon-notext ui-icon-delete"
                    }).appendTo(header);
                }

                $.extend(this, {
                    selectId: selectId,
                    menuId: menuId,
                    popupId: popupId,
                    dialogId: dialogId,
                    thisPage: thisPage,
                    menuPage: menuPage,
                    label: label,
                    isMultiple: isMultiple,
                    theme: o.theme,
                    listbox: listbox,
                    list: list,
                    header: header,
                    headerTitle: headerTitle,
                    headerClose: headerClose,
                    menuPageContent: menuPageContent,
                    menuPageClose: menuPageClose,
                    placeholder: ""
                });

                // Create list from select, update state
                this.refresh();

                if (this._origTabIndex === undefined) {
                    // Map undefined to false, because this._origTabIndex === undefined
                    // indicates that we have not yet checked whether the select has
                    // originally had a tabindex attribute, whereas false indicates that
                    // we have checked the select for such an attribute, and have found
                    // none present.
                    this._origTabIndex = (this.select[0].getAttribute("tabindex") === null) ? false : this.select.attr("tabindex");
                }
                this.select.attr("tabindex", "-1");
                this._on(this.select, { focus: "_handleSelectFocus" });

                // Button events
                this._on(this.button, {
                    vclick: "_handleButtonVclickKeydown"
                });

                // Events for list items
                this.list.attr("role", "listbox");
                this._on(this.list, {
                    "focusin": "_handleListFocus",
                    "focusout": "_handleListFocus",
                    "keydown": "_handleListKeydown",
                    "click li:not(.ui-disabled,.ui-state-disabled,.ui-li-divider)": "_handleListItemClick"
                });

                // button refocus ensures proper height calculation
                // by removing the inline style and ensuring page inclusion
                this._on(this.menuPage, { pagehide: "_handleMenuPageHide" });

                // Events on the popup
                this._on(this.listbox, { popupafterclose: "_popupClosed" });

                // Close button on small overlays
                if (this.isMultiple) {
                    this._on(this.headerClose, { click: "_handleHeaderCloseClick" });
                }

                return this;
            },

            _popupClosed: function () {
                this.close();
                this._delayedTrigger();
            },

            _delayedTrigger: function () {
                if (this._triggerChange) {
                    this.element.trigger("change");
                }
                this._triggerChange = false;
            },

            _isRebuildRequired: function () {
                var list = this.list.find("li"),
                    options = this._selectOptions().not(".ui-screen-hidden");

                // TODO exceedingly naive method to determine difference
                // ignores value changes etc in favor of a forcedRebuild
                // from the user in the refresh method
                return options.text() !== list.text();
            },

            selected: function () {
                return this._selectOptions().filter(":selected:not( :jqmData(placeholder='true') )");
            },

            refresh: function (force) {
                var self, indices;

                if (this.options.nativeMenu) {
                    return this._super(force);
                }

                self = this;
                if (force || this._isRebuildRequired()) {
                    self._buildList();
                }

                indices = this.selectedIndices();

                self.setButtonText();
                self.setButtonCount();

                self.list.find("li:not(.ui-li-divider)")
                    .find("a").removeClass($.mobile.activeBtnClass).end()
                    .attr("aria-selected", false)
                    .each(function (i) {
                        var item = $(this);
                        if ($.inArray(i, indices) > -1) {

                            // Aria selected attr
                            item.attr("aria-selected", true);

                            // Multiple selects: add the "on" checkbox state to the icon
                            if (self.isMultiple) {
                                item.find("a").removeClass("ui-checkbox-off").addClass("ui-checkbox-on");
                            } else {
                                if (item.hasClass("ui-screen-hidden")) {
                                    item.next().find("a").addClass($.mobile.activeBtnClass);
                                } else {
                                    item.find("a").addClass($.mobile.activeBtnClass);
                                }
                            }
                        } else if (self.isMultiple) {
                            item.find("a").removeClass("ui-checkbox-on").addClass("ui-checkbox-off");
                        }
                    });
            },

            close: function () {
                if (this.options.disabled || !this.isOpen) {
                    return;
                }

                var self = this;

                if (self.menuType === "page") {
                    self.menuPage.dialog("close");
                    self.list.appendTo(self.listbox);
                } else {
                    self.listbox.popup("close");
                }

                self._focusButton();
                // allow the dialog to be closed again
                self.isOpen = false;
            },

            open: function () {
                this.button.click();
            },

            _focusMenuItem: function () {
                var selector = this.list.find("a." + $.mobile.activeBtnClass);
                if (selector.length === 0) {
                    selector = this.list.find("li:not(" + unfocusableItemSelector + ") a.ui-btn");
                }
                selector.first().focus();
            },

            _decideFormat: function () {
                var self = this,
                    $window = this.window,
                    selfListParent = self.list.parent(),
                    menuHeight = selfListParent.outerHeight(),
                    scrollTop = $window.scrollTop(),
                    btnOffset = self.button.offset().top,
                    screenHeight = $window.height();

                if (menuHeight > screenHeight - 80 || !$.support.scrollTop) {

                    self.menuPage.appendTo($.mobile.pageContainer).page();
                    self.menuPageContent = self.menuPage.find(".ui-content");
                    self.menuPageClose = self.menuPage.find(".ui-header a");

                    // prevent the parent page from being removed from the DOM,
                    // otherwise the results of selecting a list item in the dialog
                    // fall into a black hole
                    self.thisPage.unbind("pagehide.remove");

                    //for WebOS/Opera Mini (set lastscroll using button offset)
                    if (scrollTop === 0 && btnOffset > screenHeight) {
                        self.thisPage.one("pagehide", function () {
                            $(this).jqmData("lastScroll", btnOffset);
                        });
                    }

                    self.menuPage.one({
                        pageshow: $.proxy(this, "_focusMenuItem"),
                        pagehide: $.proxy(this, "close")
                    });

                    self.menuType = "page";
                    self.menuPageContent.append(self.list);
                    self.menuPage
                        .find("div .ui-title")
                            .text(self.label.getEncodedText() || self.placeholder);
                } else {
                    self.menuType = "overlay";

                    self.listbox.one({ popupafteropen: $.proxy(this, "_focusMenuItem") });
                }
            },

            _buildList: function () {
                var self = this,
                    o = this.options,
                    placeholder = this.placeholder,
                    needPlaceholder = true,
                    dataIcon = "false",
                    $options, numOptions, select,
                    dataPrefix = "data-" + $.mobile.ns,
                    dataIndexAttr = dataPrefix + "option-index",
                    dataIconAttr = dataPrefix + "icon",
                    dataRoleAttr = dataPrefix + "role",
                    dataPlaceholderAttr = dataPrefix + "placeholder",
                    fragment = document.createDocumentFragment(),
                    isPlaceholderItem = false,
                    optGroup,
                    i,
                    option, $option, parent, text, anchor, classes,
                    optLabel, divider, item;

                self.list.empty().filter(".ui-listview").listview("destroy");
                $options = this._selectOptions();
                numOptions = $options.length;
                select = this.select[0];

                for (i = 0; i < numOptions; i++, isPlaceholderItem = false) {
                    option = $options[i];
                    $option = $(option);

                    // Do not create options based on ui-screen-hidden select options
                    if ($option.hasClass("ui-screen-hidden")) {
                        continue;
                    }

                    parent = option.parentNode;
                    classes = [];

                    // Although using .text() here raises the risk that, when we later paste this into the
                    // list item we end up pasting possibly malicious things like <script> tags, that risk
                    // only arises if we do something like $( "<li><a href='#'>" + text + "</a></li>" ). We
                    // don't do that. We do document.createTextNode( text ) instead, which guarantees that
                    // whatever we paste in will end up as text, with characters like <, > and & escaped.
                    text = $option.text();
                    anchor = document.createElement("a");
                    anchor.setAttribute("href", "#");
                    anchor.appendChild(document.createTextNode(text));

                    // Are we inside an optgroup?
                    if (parent !== select && parent.nodeName.toLowerCase() === "optgroup") {
                        optLabel = parent.getAttribute("label");
                        if (optLabel !== optGroup) {
                            divider = document.createElement("li");
                            divider.setAttribute(dataRoleAttr, "list-divider");
                            divider.setAttribute("role", "option");
                            divider.setAttribute("tabindex", "-1");
                            divider.appendChild(document.createTextNode(optLabel));
                            fragment.appendChild(divider);
                            optGroup = optLabel;
                        }
                    }

                    if (needPlaceholder && (!option.getAttribute("value") || text.length === 0 || $option.jqmData("placeholder"))) {
                        needPlaceholder = false;
                        isPlaceholderItem = true;

                        // If we have identified a placeholder, record the fact that it was
                        // us who have added the placeholder to the option and mark it
                        // retroactively in the select as well
                        if (null === option.getAttribute(dataPlaceholderAttr)) {
                            this._removePlaceholderAttr = true;
                        }
                        option.setAttribute(dataPlaceholderAttr, true);
                        if (o.hidePlaceholderMenuItems) {
                            classes.push("ui-screen-hidden");
                        }
                        if (placeholder !== text) {
                            placeholder = self.placeholder = text;
                        }
                    }

                    item = document.createElement("li");
                    if (option.disabled) {
                        classes.push("ui-state-disabled");
                        item.setAttribute("aria-disabled", true);
                    }
                    item.setAttribute(dataIndexAttr, i);
                    item.setAttribute(dataIconAttr, dataIcon);
                    if (isPlaceholderItem) {
                        item.setAttribute(dataPlaceholderAttr, true);
                    }
                    item.className = classes.join(" ");
                    item.setAttribute("role", "option");
                    anchor.setAttribute("tabindex", "-1");
                    if (this.isMultiple) {
                        $(anchor).addClass("ui-btn ui-checkbox-off ui-btn-icon-right");
                    }

                    item.appendChild(anchor);
                    fragment.appendChild(item);
                }

                self.list[0].appendChild(fragment);

                // Hide header if it's not a multiselect and there's no placeholder
                if (!this.isMultiple && !placeholder.length) {
                    this.header.addClass("ui-screen-hidden");
                } else {
                    this.headerTitle.text(this.placeholder);
                }

                // Now populated, create listview
                self.list.listview();
            },

            _button: function () {
                return this.options.nativeMenu ?
                    this._super() :
                    $("<a>", {
                        "href": "#",
                        "role": "button",
                        // TODO value is undefined at creation
                        "id": this.buttonId,
                        "aria-haspopup": "true",

                        // TODO value is undefined at creation
                        "aria-owns": this.menuId
                    });
            },

            _destroy: function () {

                if (!this.options.nativeMenu) {
                    this.close();

                    // Restore the tabindex attribute to its original value
                    if (this._origTabIndex !== undefined) {
                        if (this._origTabIndex !== false) {
                            this.select.attr("tabindex", this._origTabIndex);
                        } else {
                            this.select.removeAttr("tabindex");
                        }
                    }

                    // Remove the placeholder attribute if we were the ones to add it
                    if (this._removePlaceholderAttr) {
                        this._selectOptions().removeAttr("data-" + $.mobile.ns + "placeholder");
                    }

                    // Remove the popup
                    this.listbox.remove();

                    // Remove the dialog
                    this.menuPage.remove();
                }

                // Chain up
                this._super();
            }
        });

    })(jQuery);

    (function ($, undefined) {

        var popup;

        function getPopup() {
            if (!popup) {
                popup = $("<div></div>", {
                    "class": "ui-slider-popup ui-shadow ui-corner-all"
                });
            }
            return popup.clone();
        }

        $.widget("mobile.slider", $.mobile.slider, {
            options: {
                popupEnabled: false,
                showValue: false
            },

            _create: function () {
                this._super();

                $.extend(this, {
                    _currentValue: null,
                    _popup: null,
                    _popupVisible: false
                });

                this._setOption("popupEnabled", this.options.popupEnabled);

                this._on(this.handle, { "vmousedown": "_showPopup" });
                this._on(this.slider.add(this.document), { "vmouseup": "_hidePopup" });
                this._refresh();
            },

            // position the popup centered 5px above the handle
            _positionPopup: function () {
                var dstOffset = this.handle.offset();

                this._popup.offset({
                    left: dstOffset.left + (this.handle.width() - this._popup.width()) / 2,
                    top: dstOffset.top - this._popup.outerHeight() - 5
                });
            },

            _setOption: function (key, value) {
                this._super(key, value);

                if (key === "showValue") {
                    this.handle.html(value && !this.options.mini ? this._value() : "");
                } else if (key === "popupEnabled") {
                    if (value && !this._popup) {
                        this._popup = getPopup()
                            .addClass("ui-body-" + (this.options.theme || "a"))
                            .hide()
                            .insertBefore(this.element);
                    }
                }
            },

            // show value on the handle and in popup
            refresh: function () {
                this._super.apply(this, arguments);
                this._refresh();
            },

            _refresh: function () {
                var o = this.options, newValue;

                if (o.popupEnabled) {
                    // remove the title attribute from the handle (which is
                    // responsible for the annoying tooltip); NB we have
                    // to do it here as the jqm slider sets it every time
                    // the slider's value changes :(
                    this.handle.removeAttr("title");
                }

                newValue = this._value();
                if (newValue === this._currentValue) {
                    return;
                }
                this._currentValue = newValue;

                if (o.popupEnabled && this._popup) {
                    this._positionPopup();
                    this._popup.html(newValue);
                }

                if (o.showValue && !this.options.mini) {
                    this.handle.html(newValue);
                }
            },

            _showPopup: function () {
                if (this.options.popupEnabled && !this._popupVisible) {
                    this.handle.html("");
                    this._popup.show();
                    this._positionPopup();
                    this._popupVisible = true;
                }
            },

            _hidePopup: function () {
                var o = this.options;

                if (o.popupEnabled && this._popupVisible) {
                    if (o.showValue && !o.mini) {
                        this.handle.html(this._value());
                    }
                    this._popup.hide();
                    this._popupVisible = false;
                }
            }
        });

    })(jQuery);

    (function ($, undefined) {

        function defaultAutodividersSelector(elt) {
            // look for the text in the given element
            var text = $.trim(elt.text()) || null;

            if (!text) {
                return null;
            }

            // create the text for the divider (first uppercased letter)
            text = text.slice(0, 1).toUpperCase();

            return text;
        }

        $.widget("mobile.listview", $.mobile.listview, {
            options: {
                autodividers: false,
                autodividersSelector: defaultAutodividersSelector
            },

            _beforeListviewRefresh: function () {
                if (this.options.autodividers) {
                    this._replaceDividers();
                    this._superApply(arguments);
                }
            },

            _replaceDividers: function () {
                var i, lis, li, dividerText,
                    lastDividerText = null,
                    list = this.element,
                    divider;

                list.children("li:jqmData(role='list-divider')").remove();

                lis = list.children("li");

                for (i = 0; i < lis.length ; i++) {
                    li = lis[i];
                    dividerText = this.options.autodividersSelector($(li));

                    if (dividerText && lastDividerText !== dividerText) {
                        divider = document.createElement("li");
                        divider.appendChild(document.createTextNode(dividerText));
                        divider.setAttribute("data-" + $.mobile.ns + "role", "list-divider");
                        li.parentNode.insertBefore(divider, li);
                    }

                    lastDividerText = dividerText;
                }
            }
        });

    })(jQuery);

    (function ($, undefined) {

        $.widget("mobile.navbar", {
            options: {
                iconpos: "top",
                grid: null
            },

            _create: function () {

                var $navbar = this.element,
                    $navbtns = $navbar.find("a, button"),
                    iconpos = $navbtns.filter(":jqmData(icon)").length ? this.options.iconpos : undefined;

                $navbar.addClass("ui-navbar")
                    .attr("role", "navigation")
                    .find("ul")
                    .jqmEnhanceable()
                    .grid({ grid: this.options.grid });

                $navbtns
                    .each(function () {
                        var icon = $.mobile.getAttribute(this, "icon"),
                            theme = $.mobile.getAttribute(this, "theme"),
                            classes = "ui-btn";

                        if (theme) {
                            classes += " ui-btn-" + theme;
                        }
                        if (icon) {
                            classes += " ui-icon-" + icon + " ui-btn-icon-" + iconpos;
                        }
                        $(this).addClass(classes);
                    });

                $navbar.delegate("a", "vclick", function ( /* event */) {
                    var activeBtn = $(this);

                    if (!(activeBtn.hasClass("ui-state-disabled") ||

                        // DEPRECATED as of 1.4.0 - remove after 1.4.0 release
                        // only ui-state-disabled should be present thereafter
                        activeBtn.hasClass("ui-disabled") ||
                        activeBtn.hasClass($.mobile.activeBtnClass))) {

                        $navbtns.removeClass($.mobile.activeBtnClass);
                        activeBtn.addClass($.mobile.activeBtnClass);

                        // The code below is a workaround to fix #1181
                        $(document).one("pagehide", function () {
                            activeBtn.removeClass($.mobile.activeBtnClass);
                        });
                    }
                });

                // Buttons in the navbar with ui-state-persist class should regain their active state before page show
                $navbar.closest(".ui-page").bind("pagebeforeshow", function () {
                    $navbtns.filter(".ui-state-persist").addClass($.mobile.activeBtnClass);
                });
            }
        });

    })(jQuery);

    (function ($, window, undefined) {

        $.widget("mobile.page", $.mobile.page, {
            options: {

                // Accepts left, right and none
                closeBtn: "left",
                closeBtnText: "Close",
                overlayTheme: "a",
                corners: true,
                dialog: false
            },

            _create: function () {
                this._super();
                if (this.options.dialog) {

                    $.extend(this, {
                        _inner: this.element.children(),
                        _headerCloseButton: null
                    });

                    if (!this.options.enhanced) {
                        this._setCloseBtn(this.options.closeBtn);
                    }
                }
            },

            _enhance: function () {
                this._super();

                // Class the markup for dialog styling and wrap interior
                if (this.options.dialog) {
                    this.element.addClass("ui-dialog")
                        .wrapInner($("<div/>", {

                            // ARIA role
                            "role": "dialog",
                            "class": "ui-dialog-contain ui-overlay-shadow" +
                                (this.options.corners ? " ui-corner-all" : "")
                        }));
                }
            },

            _setOptions: function (options) {
                var closeButtonLocation, closeButtonText,
                    currentOpts = this.options;

                if (options.corners !== undefined) {
                    this._inner.toggleClass("ui-corner-all", !!options.corners);
                }

                if (options.overlayTheme !== undefined) {
                    if ($.mobile.activePage[0] === this.element[0]) {
                        currentOpts.overlayTheme = options.overlayTheme;
                        this._handlePageBeforeShow();
                    }
                }

                if (options.closeBtnText !== undefined) {
                    closeButtonLocation = currentOpts.closeBtn;
                    closeButtonText = options.closeBtnText;
                }

                if (options.closeBtn !== undefined) {
                    closeButtonLocation = options.closeBtn;
                }

                if (closeButtonLocation) {
                    this._setCloseBtn(closeButtonLocation, closeButtonText);
                }

                this._super(options);
            },

            _handlePageBeforeShow: function () {
                if (this.options.overlayTheme && this.options.dialog) {
                    this.removeContainerBackground();
                    this.setContainerBackground(this.options.overlayTheme);
                } else {
                    this._super();
                }
            },

            _setCloseBtn: function (location, text) {
                var dst,
                    btn = this._headerCloseButton;

                // Sanitize value
                location = "left" === location ? "left" : "right" === location ? "right" : "none";

                if ("none" === location) {
                    if (btn) {
                        btn.remove();
                        btn = null;
                    }
                } else if (btn) {
                    btn.removeClass("ui-btn-left ui-btn-right").addClass("ui-btn-" + location);
                    if (text) {
                        btn.text(text);
                    }
                } else {
                    dst = this._inner.find(":jqmData(role='header')").first();
                    btn = $("<a></a>", {
                        "href": "#",
                        "class": "ui-btn ui-corner-all ui-icon-delete ui-btn-icon-notext ui-btn-" + location
                    })
                        .attr("data-" + $.mobile.ns + "rel", "back")
                        .text(text || this.options.closeBtnText || "")
                        .prependTo(dst);
                }

                this._headerCloseButton = btn;
            }
        });

    })(jQuery, this);

    (function ($, undefined) {

        $.widget("mobile.panel", {
            options: {
                classes: {
                    panel: "ui-panel",
                    panelOpen: "ui-panel-open",
                    panelClosed: "ui-panel-closed",
                    panelFixed: "ui-panel-fixed",
                    panelInner: "ui-panel-inner",
                    modal: "ui-panel-dismiss",
                    modalOpen: "ui-panel-dismiss-open",
                    pageContainer: "ui-panel-page-container",
                    pageWrapper: "ui-panel-wrapper",
                    pageFixedToolbar: "ui-panel-fixed-toolbar",
                    pageContentPrefix: "ui-panel-page-content", /* Used for wrapper and fixed toolbars position, display and open classes. */
                    animate: "ui-panel-animate"
                },
                animate: true,
                theme: null,
                position: "left",
                dismissible: true,
                display: "reveal", //accepts reveal, push, overlay
                swipeClose: true,
                positionFixed: false
            },

            _closeLink: null,
            _parentPage: null,
            _page: null,
            _modal: null,
            _panelInner: null,
            _wrapper: null,
            _fixedToolbars: null,

            _create: function () {
                var el = this.element,
                    parentPage = el.closest(".ui-page, :jqmData(role='page')");

                // expose some private props to other methods
                $.extend(this, {
                    _closeLink: el.find(":jqmData(rel='close')"),
                    _parentPage: (parentPage.length > 0) ? parentPage : false,
                    _openedPage: null,
                    _page: this._getPage,
                    _panelInner: this._getPanelInner(),
                    _fixedToolbars: this._getFixedToolbars
                });
                if (this.options.display !== "overlay") {
                    this._getWrapper();
                }
                this._addPanelClasses();

                // if animating, add the class to do so
                if ($.support.cssTransform3d && !!this.options.animate) {
                    this.element.addClass(this.options.classes.animate);
                }

                this._bindUpdateLayout();
                this._bindCloseEvents();
                this._bindLinkListeners();
                this._bindPageEvents();

                if (!!this.options.dismissible) {
                    this._createModal();
                }

                this._bindSwipeEvents();
            },

            _getPanelInner: function () {
                var panelInner = this.element.find("." + this.options.classes.panelInner);

                if (panelInner.length === 0) {
                    panelInner = this.element.children().wrapAll("<div class='" + this.options.classes.panelInner + "' />").parent();
                }

                return panelInner;
            },

            _createModal: function () {
                var self = this,
                    target = self._parentPage ? self._parentPage.parent() : self.element.parent();

                self._modal = $("<div class='" + self.options.classes.modal + "'></div>")
                    .on("mousedown", function () {
                        self.close();
                    })
                    .appendTo(target);
            },

            _getPage: function () {
                var page = this._openedPage || this._parentPage || $("." + $.mobile.activePageClass);

                return page;
            },

            _getWrapper: function () {
                var wrapper = this._page().find("." + this.options.classes.pageWrapper);
                if (wrapper.length === 0) {
                    wrapper = this._page().children(".ui-header:not(.ui-header-fixed), .ui-content:not(.ui-popup), .ui-footer:not(.ui-footer-fixed)")
                        .wrapAll("<div class='" + this.options.classes.pageWrapper + "'></div>")
                        .parent();
                }

                this._wrapper = wrapper;
            },

            _getFixedToolbars: function () {
                var extFixedToolbars = $("body").children(".ui-header-fixed, .ui-footer-fixed"),
                    intFixedToolbars = this._page().find(".ui-header-fixed, .ui-footer-fixed"),
                    fixedToolbars = extFixedToolbars.add(intFixedToolbars).addClass(this.options.classes.pageFixedToolbar);

                return fixedToolbars;
            },

            _getPosDisplayClasses: function (prefix) {
                return prefix + "-position-" + this.options.position + " " + prefix + "-display-" + this.options.display;
            },

            _getPanelClasses: function () {
                var panelClasses = this.options.classes.panel +
                    " " + this._getPosDisplayClasses(this.options.classes.panel) +
                    " " + this.options.classes.panelClosed +
                    " " + "ui-body-" + (this.options.theme ? this.options.theme : "inherit");

                if (!!this.options.positionFixed) {
                    panelClasses += " " + this.options.classes.panelFixed;
                }

                return panelClasses;
            },

            _addPanelClasses: function () {
                this.element.addClass(this._getPanelClasses());
            },

            _handleCloseClick: function (event) {
                if (!event.isDefaultPrevented()) {
                    this.close();
                }
            },

            _bindCloseEvents: function () {
                this._on(this._closeLink, {
                    "click": "_handleCloseClick"
                });

                this._on({
                    "click a:jqmData(ajax='false')": "_handleCloseClick"
                });
            },

            _positionPanel: function (scrollToTop) {
                var self = this,
                    panelInnerHeight = self._panelInner.outerHeight(),
                    expand = panelInnerHeight > $.mobile.getScreenHeight();

                if (expand || !self.options.positionFixed) {
                    if (expand) {
                        self._unfixPanel();
                        $.mobile.resetActivePageHeight(panelInnerHeight);
                    }
                    if (scrollToTop) {
                        this.window[0].scrollTo(0, $.mobile.defaultHomeScroll);
                    }
                } else {
                    self._fixPanel();
                }
            },

            _bindFixListener: function () {
                this._on($(window), { "throttledresize": "_positionPanel" });
            },

            _unbindFixListener: function () {
                this._off($(window), "throttledresize");
            },

            _unfixPanel: function () {
                if (!!this.options.positionFixed && $.support.fixedPosition) {
                    this.element.removeClass(this.options.classes.panelFixed);
                }
            },

            _fixPanel: function () {
                if (!!this.options.positionFixed && $.support.fixedPosition) {
                    this.element.addClass(this.options.classes.panelFixed);
                }
            },

            _bindUpdateLayout: function () {
                var self = this;

                self.element.on("updatelayout", function (/* e */) {
                    if (self._open) {
                        self._positionPanel();
                    }
                });
            },

            _bindLinkListeners: function () {
                this._on("body", {
                    "click a": "_handleClick"
                });

            },

            _handleClick: function (e) {
                var link,
                    panelId = this.element.attr("id");

                if (e.currentTarget.href.split("#")[1] === panelId && panelId !== undefined) {

                    e.preventDefault();
                    link = $(e.target);
                    if (link.hasClass("ui-btn")) {
                        link.addClass($.mobile.activeBtnClass);
                        this.element.one("panelopen panelclose", function () {
                            link.removeClass($.mobile.activeBtnClass);
                        });
                    }
                    this.toggle();
                }
            },

            _bindSwipeEvents: function () {
                var self = this,
                    area = self._modal ? self.element.add(self._modal) : self.element;

                // on swipe, close the panel
                if (!!self.options.swipeClose) {
                    if (self.options.position === "left") {
                        area.on("swipeleft.panel", function (/* e */) {
                            self.close();
                        });
                    } else {
                        area.on("swiperight.panel", function (/* e */) {
                            self.close();
                        });
                    }
                }
            },

            _bindPageEvents: function () {
                var self = this;

                this.document
                    // Close the panel if another panel on the page opens
                    .on("panelbeforeopen", function (e) {
                        if (self._open && e.target !== self.element[0]) {
                            self.close();
                        }
                    })
                    // On escape, close? might need to have a target check too...
                    .on("keyup.panel", function (e) {
                        if (e.keyCode === 27 && self._open) {
                            self.close();
                        }
                    });
                if (!this._parentPage && this.options.display !== "overlay") {
                    this._on(this.document, {
                        "pageshow": function () {
                            this._openedPage = null;
                            this._getWrapper();
                        }
                    });
                }
                // Clean up open panels after page hide
                if (self._parentPage) {
                    this.document.on("pagehide", ":jqmData(role='page')", function () {
                        if (self._open) {
                            self.close(true);
                        }
                    });
                } else {
                    this.document.on("pagebeforehide", function () {
                        if (self._open) {
                            self.close(true);
                        }
                    });
                }
            },

            // state storage of open or closed
            _open: false,
            _pageContentOpenClasses: null,
            _modalOpenClasses: null,

            open: function (immediate) {
                if (!this._open) {
                    var self = this,
                        o = self.options,

                        _openPanel = function () {
                            self._off(self.document, "panelclose");
                            self._page().jqmData("panel", "open");

                            if ($.support.cssTransform3d && !!o.animate && o.display !== "overlay") {
                                self._wrapper.addClass(o.classes.animate);
                                self._fixedToolbars().addClass(o.classes.animate);
                            }

                            if (!immediate && $.support.cssTransform3d && !!o.animate) {
                                (self._wrapper || self.element)
                                    .animationComplete(complete, "transition");
                            } else {
                                setTimeout(complete, 0);
                            }

                            if (o.theme && o.display !== "overlay") {
                                self._page().parent()
                                    .addClass(o.classes.pageContainer + "-themed " + o.classes.pageContainer + "-" + o.theme);
                            }

                            self.element
                                .removeClass(o.classes.panelClosed)
                                .addClass(o.classes.panelOpen);

                            self._positionPanel(true);

                            self._pageContentOpenClasses = self._getPosDisplayClasses(o.classes.pageContentPrefix);

                            if (o.display !== "overlay") {
                                self._page().parent().addClass(o.classes.pageContainer);
                                self._wrapper.addClass(self._pageContentOpenClasses);
                                self._fixedToolbars().addClass(self._pageContentOpenClasses);
                            }

                            self._modalOpenClasses = self._getPosDisplayClasses(o.classes.modal) + " " + o.classes.modalOpen;
                            if (self._modal) {
                                self._modal
                                    .addClass(self._modalOpenClasses)
                                    .height(Math.max(self._modal.height(), self.document.height()));
                            }
                        },
                        complete = function () {

                            // Bail if the panel was closed before the opening animation has completed
                            if (!self._open) {
                                return;
                            }

                            if (o.display !== "overlay") {
                                self._wrapper.addClass(o.classes.pageContentPrefix + "-open");
                                self._fixedToolbars().addClass(o.classes.pageContentPrefix + "-open");
                            }

                            self._bindFixListener();

                            self._trigger("open");

                            self._openedPage = self._page();
                        };

                    self._trigger("beforeopen");

                    if (self._page().jqmData("panel") === "open") {
                        self._on(self.document, {
                            "panelclose": _openPanel
                        });
                    } else {
                        _openPanel();
                    }

                    self._open = true;
                }
            },

            close: function (immediate) {
                if (this._open) {
                    var self = this,
                        o = this.options,

                        _closePanel = function () {

                            self.element.removeClass(o.classes.panelOpen);

                            if (o.display !== "overlay") {
                                self._wrapper.removeClass(self._pageContentOpenClasses);
                                self._fixedToolbars().removeClass(self._pageContentOpenClasses);
                            }

                            if (!immediate && $.support.cssTransform3d && !!o.animate) {
                                (self._wrapper || self.element)
                                    .animationComplete(complete, "transition");
                            } else {
                                setTimeout(complete, 0);
                            }

                            if (self._modal) {
                                self._modal
                                    .removeClass(self._modalOpenClasses)
                                    .height("");
                            }
                        },
                        complete = function () {
                            if (o.theme && o.display !== "overlay") {
                                self._page().parent().removeClass(o.classes.pageContainer + "-themed " + o.classes.pageContainer + "-" + o.theme);
                            }

                            self.element.addClass(o.classes.panelClosed);

                            if (o.display !== "overlay") {
                                self._page().parent().removeClass(o.classes.pageContainer);
                                self._wrapper.removeClass(o.classes.pageContentPrefix + "-open");
                                self._fixedToolbars().removeClass(o.classes.pageContentPrefix + "-open");
                            }

                            if ($.support.cssTransform3d && !!o.animate && o.display !== "overlay") {
                                self._wrapper.removeClass(o.classes.animate);
                                self._fixedToolbars().removeClass(o.classes.animate);
                            }

                            self._fixPanel();
                            self._unbindFixListener();
                            $.mobile.resetActivePageHeight();

                            self._page().jqmRemoveData("panel");

                            self._trigger("close");

                            self._openedPage = null;
                        };

                    self._trigger("beforeclose");

                    _closePanel();

                    self._open = false;
                }
            },

            toggle: function () {
                this[this._open ? "close" : "open"]();
            },

            _destroy: function () {
                var otherPanels,
                o = this.options,
                multiplePanels = ($("body > :mobile-panel").length + $.mobile.activePage.find(":mobile-panel").length) > 1;

                if (o.display !== "overlay") {

                    //  remove the wrapper if not in use by another panel
                    otherPanels = $("body > :mobile-panel").add($.mobile.activePage.find(":mobile-panel"));
                    if (otherPanels.not(".ui-panel-display-overlay").not(this.element).length === 0) {
                        this._wrapper.children().unwrap();
                    }

                    if (this._open) {

                        this._fixedToolbars().removeClass(o.classes.pageContentPrefix + "-open");

                        if ($.support.cssTransform3d && !!o.animate) {
                            this._fixedToolbars().removeClass(o.classes.animate);
                        }

                        this._page().parent().removeClass(o.classes.pageContainer);

                        if (o.theme) {
                            this._page().parent().removeClass(o.classes.pageContainer + "-themed " + o.classes.pageContainer + "-" + o.theme);
                        }
                    }
                }

                if (!multiplePanels) {

                    this.document.off("panelopen panelclose");

                }

                if (this._open) {
                    this._page().jqmRemoveData("panel");
                }

                this._panelInner.children().unwrap();

                this.element
                    .removeClass([this._getPanelClasses(), o.classes.panelOpen, o.classes.animate].join(" "))
                    .off("swipeleft.panel swiperight.panel")
                    .off("panelbeforeopen")
                    .off("panelhide")
                    .off("keyup.panel")
                    .off("updatelayout");

                if (this._modal) {
                    this._modal.remove();
                }
            }
        });

    })(jQuery);


    (function ($, undefined) {

        var ieHack = ($.mobile.browser.oldIE && $.mobile.browser.oldIE <= 8),
            uiTemplate = $(
                "<div class='ui-popup-arrow-guide'></div>" +
                "<div class='ui-popup-arrow-container" + (ieHack ? " ie" : "") + "'>" +
                    "<div class='ui-popup-arrow'></div>" +
                "</div>"
            );

        function getArrow() {
            var clone = uiTemplate.clone(),
                gd = clone.eq(0),
                ct = clone.eq(1),
                ar = ct.children();

            return { arEls: ct.add(gd), gd: gd, ct: ct, ar: ar };
        }

        $.widget("mobile.popup", $.mobile.popup, {
            options: {

                arrow: ""
            },

            _create: function () {
                var ar,
                    ret = this._super();

                if (this.options.arrow) {
                    this._ui.arrow = ar = this._addArrow();
                }

                return ret;
            },

            _addArrow: function () {
                var theme,
                    opts = this.options,
                    ar = getArrow();

                theme = this._themeClassFromOption("ui-body-", opts.theme);
                ar.ar.addClass(theme + (opts.shadow ? " ui-overlay-shadow" : ""));
                ar.arEls.hide().appendTo(this.element);

                return ar;
            },

            _unenhance: function () {
                var ar = this._ui.arrow;

                if (ar) {
                    ar.arEls.remove();
                }

                return this._super();
            },

            // Pretend to show an arrow described by @p and @dir and calculate the
            // distance from the desired point. If a best-distance is passed in, return
            // the minimum of the one passed in and the one calculated.
            _tryAnArrow: function (p, dir, desired, s, best) {
                var result, r, diff, desiredForArrow = {}, tip = {};

                // If the arrow has no wiggle room along the edge of the popup, it cannot
                // be displayed along the requested edge without it sticking out.
                if (s.arFull[p.dimKey] > s.guideDims[p.dimKey]) {
                    return best;
                }

                desiredForArrow[p.fst] = desired[p.fst] +
                    (s.arHalf[p.oDimKey] + s.menuHalf[p.oDimKey]) * p.offsetFactor -
                    s.contentBox[p.fst] + (s.clampInfo.menuSize[p.oDimKey] - s.contentBox[p.oDimKey]) * p.arrowOffsetFactor;
                desiredForArrow[p.snd] = desired[p.snd];

                result = s.result || this._calculateFinalLocation(desiredForArrow, s.clampInfo);
                r = { x: result.left, y: result.top };

                tip[p.fst] = r[p.fst] + s.contentBox[p.fst] + p.tipOffset;
                tip[p.snd] = Math.max(result[p.prop] + s.guideOffset[p.prop] + s.arHalf[p.dimKey],
                    Math.min(result[p.prop] + s.guideOffset[p.prop] + s.guideDims[p.dimKey] - s.arHalf[p.dimKey],
                        desired[p.snd]));

                diff = Math.abs(desired.x - tip.x) + Math.abs(desired.y - tip.y);
                if (!best || diff < best.diff) {
                    // Convert tip offset to coordinates inside the popup
                    tip[p.snd] -= s.arHalf[p.dimKey] + result[p.prop] + s.contentBox[p.snd];
                    best = { dir: dir, diff: diff, result: result, posProp: p.prop, posVal: tip[p.snd] };
                }

                return best;
            },

            _getPlacementState: function (clamp) {
                var offset, gdOffset,
                    ar = this._ui.arrow,
                    state = {
                        clampInfo: this._clampPopupWidth(!clamp),
                        arFull: { cx: ar.ct.width(), cy: ar.ct.height() },
                        guideDims: { cx: ar.gd.width(), cy: ar.gd.height() },
                        guideOffset: ar.gd.offset()
                    };

                offset = this.element.offset();

                ar.gd.css({ left: 0, top: 0, right: 0, bottom: 0 });
                gdOffset = ar.gd.offset();
                state.contentBox = {
                    x: gdOffset.left - offset.left,
                    y: gdOffset.top - offset.top,
                    cx: ar.gd.width(),
                    cy: ar.gd.height()
                };
                ar.gd.removeAttr("style");

                // The arrow box moves between guideOffset and guideOffset + guideDims - arFull
                state.guideOffset = { left: state.guideOffset.left - offset.left, top: state.guideOffset.top - offset.top };
                state.arHalf = { cx: state.arFull.cx / 2, cy: state.arFull.cy / 2 };
                state.menuHalf = { cx: state.clampInfo.menuSize.cx / 2, cy: state.clampInfo.menuSize.cy / 2 };

                return state;
            },

            _placementCoords: function (desired) {
                var state, best, params, elOffset, bgRef,
                    optionValue = this.options.arrow,
                    ar = this._ui.arrow;

                if (!ar) {
                    return this._super(desired);
                }

                ar.arEls.show();

                bgRef = {};
                state = this._getPlacementState(true);
                params = {
                    "l": { fst: "x", snd: "y", prop: "top", dimKey: "cy", oDimKey: "cx", offsetFactor: 1, tipOffset: -state.arHalf.cx, arrowOffsetFactor: 0 },
                    "r": { fst: "x", snd: "y", prop: "top", dimKey: "cy", oDimKey: "cx", offsetFactor: -1, tipOffset: state.arHalf.cx + state.contentBox.cx, arrowOffsetFactor: 1 },
                    "b": { fst: "y", snd: "x", prop: "left", dimKey: "cx", oDimKey: "cy", offsetFactor: -1, tipOffset: state.arHalf.cy + state.contentBox.cy, arrowOffsetFactor: 1 },
                    "t": { fst: "y", snd: "x", prop: "left", dimKey: "cx", oDimKey: "cy", offsetFactor: 1, tipOffset: -state.arHalf.cy, arrowOffsetFactor: 0 }
                };

                // Try each side specified in the options to see on which one the arrow
                // should be placed such that the distance between the tip of the arrow and
                // the desired coordinates is the shortest.
                $.each((optionValue === true ? "l,t,r,b" : optionValue).split(","),
                    $.proxy(function (key, value) {
                        best = this._tryAnArrow(params[value], value, desired, state, best);
                    }, this));

                // Could not place the arrow along any of the edges - behave as if showing
                // the arrow was turned off.
                if (!best) {
                    ar.arEls.hide();
                    return this._super(desired);
                }

                // Move the arrow into place
                ar.ct
                    .removeClass("ui-popup-arrow-l ui-popup-arrow-t ui-popup-arrow-r ui-popup-arrow-b")
                    .addClass("ui-popup-arrow-" + best.dir)
                    .removeAttr("style").css(best.posProp, best.posVal)
                    .show();

                // Do not move/size the background div on IE, because we use the arrow div for background as well.
                if (!ieHack) {
                    elOffset = this.element.offset();
                    bgRef[params[best.dir].fst] = ar.ct.offset();
                    bgRef[params[best.dir].snd] = {
                        left: elOffset.left + state.contentBox.x,
                        top: elOffset.top + state.contentBox.y
                    };
                }

                return best.result;
            },

            _setOptions: function (opts) {
                var newTheme,
                    oldTheme = this.options.theme,
                    ar = this._ui.arrow,
                    ret = this._super(opts);

                if (opts.arrow !== undefined) {
                    if (!ar && opts.arrow) {
                        this._ui.arrow = this._addArrow();

                        // Important to return here so we don't set the same options all over
                        // again below.
                        return;
                    } else if (ar && !opts.arrow) {
                        ar.arEls.remove();
                        this._ui.arrow = null;
                    }
                }

                // Reassign with potentially new arrow
                ar = this._ui.arrow;

                if (ar) {
                    if (opts.theme !== undefined) {
                        oldTheme = this._themeClassFromOption("ui-body-", oldTheme);
                        newTheme = this._themeClassFromOption("ui-body-", opts.theme);
                        ar.ar.removeClass(oldTheme).addClass(newTheme);
                    }

                    if (opts.shadow !== undefined) {
                        ar.ar.toggleClass("ui-overlay-shadow", opts.shadow);
                    }
                }

                return ret;
            },

            _destroy: function () {
                var ar = this._ui.arrow;

                if (ar) {
                    ar.arEls.remove();
                }

                return this._super();
            }
        });

    })(jQuery);


    (function ($, undefined) {

        $.widget("mobile.table", {
            options: {
                classes: {
                    table: "ui-table"
                },
                enhanced: false
            },

            _create: function () {
                if (!this.options.enhanced) {
                    this.element.addClass(this.options.classes.table);
                }

                // extend here, assign on refresh > _setHeaders
                $.extend(this, {

                    // Expose headers and allHeaders properties on the widget
                    // headers references the THs within the first TR in the table
                    headers: undefined,

                    // allHeaders references headers, plus all THs in the thead, which may
                    // include several rows, or not
                    allHeaders: undefined
                });

                this._refresh(true);
            },

            _setHeaders: function () {
                var trs = this.element.find("thead tr");

                this.headers = this.element.find("tr:eq(0)").children();
                this.allHeaders = this.headers.add(trs.children());
            },

            refresh: function () {
                this._refresh();
            },

            rebuild: $.noop,

            _refresh: function ( /* create */) {
                var table = this.element,
                    trs = table.find("thead tr");

                // updating headers on refresh (fixes #5880)
                this._setHeaders();

                // Iterate over the trs
                trs.each(function () {
                    var columnCount = 0;

                    // Iterate over the children of the tr
                    $(this).children().each(function () {
                        var span = parseInt(this.getAttribute("colspan"), 10),
                            selector = ":nth-child(" + (columnCount + 1) + ")",
                            j;

                        this.setAttribute("data-" + $.mobile.ns + "colstart", columnCount + 1);

                        if (span) {
                            for (j = 0; j < span - 1; j++) {
                                columnCount++;
                                selector += ", :nth-child(" + (columnCount + 1) + ")";
                            }
                        }

                        // Store "cells" data on header as a reference to all cells in the
                        // same column as this TH
                        $(this).jqmData("cells", table.find("tr").not(trs.eq(0)).not(this).children(selector));

                        columnCount++;
                    });
                });
            }
        });

    })(jQuery);


    (function ($, undefined) {

        $.widget("mobile.table", $.mobile.table, {
            options: {
                mode: "columntoggle",
                columnBtnTheme: null,
                columnPopupTheme: null,
                columnBtnText: "Columns...",
                classes: $.extend($.mobile.table.prototype.options.classes, {
                    popup: "ui-table-columntoggle-popup",
                    columnBtn: "ui-table-columntoggle-btn",
                    priorityPrefix: "ui-table-priority-",
                    columnToggleTable: "ui-table-columntoggle"
                })
            },

            _create: function () {
                this._super();

                if (this.options.mode !== "columntoggle") {
                    return;
                }

                $.extend(this, {
                    _menu: null
                });

                if (this.options.enhanced) {
                    this._menu = $(this.document[0].getElementById(this._id() + "-popup")).children().first();
                    this._addToggles(this._menu, true);
                } else {
                    this._menu = this._enhanceColToggle();
                    this.element.addClass(this.options.classes.columnToggleTable);
                }

                this._setupEvents();

                this._setToggleState();
            },

            _id: function () {
                return (this.element.attr("id") || (this.widgetName + this.uuid));
            },

            _setupEvents: function () {
                //NOTE: inputs are bound in bindToggles,
                // so it can be called on refresh, too

                // update column toggles on resize
                this._on(this.window, {
                    throttledresize: "_setToggleState"
                });
                this._on(this._menu, {
                    "change input": "_menuInputChange"
                });
            },

            _addToggles: function (menu, keep) {
                var inputs,
                    checkboxIndex = 0,
                    opts = this.options,
                    container = menu.controlgroup("container");

                // allow update of menu on refresh (fixes #5880)
                if (keep) {
                    inputs = menu.find("input");
                } else {
                    container.empty();
                }

                // create the hide/show toggles
                this.headers.not("td").each(function () {
                    var input, cells,
                        header = $(this),
                        priority = $.mobile.getAttribute(this, "priority");

                    if (priority) {
                        cells = header.add(header.jqmData("cells"));
                        cells.addClass(opts.classes.priorityPrefix + priority);

                        // Make sure the (new?) checkbox is associated with its header via .jqmData() and
                        // that, vice versa, the header is also associated with the checkbox
                        input = (keep ? inputs.eq(checkboxIndex++) :
                            $("<label><input type='checkbox' checked />" +
                                (header.children("abbr").first().attr("title") ||
                                    header.text()) +
                                "</label>")
                                .appendTo(container)
                                .children(0)
                                .checkboxradio({
                                    theme: opts.columnPopupTheme
                                }))

                                // Associate the header with the checkbox
                                .jqmData("header", header)
                                .jqmData("cells", cells);

                        // Associate the checkbox with the header
                        header.jqmData("input", input);
                    }
                });

                // set bindings here
                if (!keep) {
                    menu.controlgroup("refresh");
                }
            },

            _menuInputChange: function (evt) {
                var input = $(evt.target),
                    checked = input[0].checked;

                input.jqmData("cells")
                    .toggleClass("ui-table-cell-hidden", !checked)
                    .toggleClass("ui-table-cell-visible", checked);
            },

            _unlockCells: function (cells) {
                // allow hide/show via CSS only = remove all toggle-locks
                cells.removeClass("ui-table-cell-hidden ui-table-cell-visible");
            },

            _enhanceColToggle: function () {
                var id, menuButton, popup, menu,
                    table = this.element,
                    opts = this.options,
                    ns = $.mobile.ns,
                    fragment = this.document[0].createDocumentFragment();

                id = this._id() + "-popup";
                menuButton = $("<a href='#" + id + "' " +
                    "class='" + opts.classes.columnBtn + " ui-btn " +
                    "ui-btn-" + (opts.columnBtnTheme || "a") +
                    " ui-corner-all ui-shadow ui-mini' " +
                    "data-" + ns + "rel='popup'>" + opts.columnBtnText + "</a>");
                popup = $("<div class='" + opts.classes.popup + "' id='" + id + "'></div>");
                menu = $("<fieldset></fieldset>").controlgroup();

                // set extension here, send "false" to trigger build/rebuild
                this._addToggles(menu, false);

                menu.appendTo(popup);

                fragment.appendChild(popup[0]);
                fragment.appendChild(menuButton[0]);
                table.before(fragment);

                popup.popup();

                return menu;
            },

            rebuild: function () {
                this._super();

                if (this.options.mode === "columntoggle") {
                    // NOTE: rebuild passes "false", while refresh passes "undefined"
                    // both refresh the table, but inside addToggles, !false will be true,
                    // so a rebuild call can be indentified
                    this._refresh(false);
                }
            },

            _refresh: function (create) {
                var headers, hiddenColumns, index;

                // Calling _super() here updates this.headers
                this._super(create);

                if (!create && this.options.mode === "columntoggle") {
                    headers = this.headers;
                    hiddenColumns = [];

                    // Find the index of the column header associated with each old checkbox among the
                    // post-refresh headers and, if the header is still there, make sure the corresponding
                    // column will be hidden if the pre-refresh checkbox indicates that the column is
                    // hidden by recording its index in the array of hidden columns.
                    this._menu.find("input").each(function () {
                        var input = $(this),
                            header = input.jqmData("header"),
                            index = headers.index(header[0]);

                        if (index > -1 && !input.prop("checked")) {

                            // The column header associated with /this/ checkbox is still present in the
                            // post-refresh table and the checkbox is not checked, so the column associated
                            // with this column header is currently hidden. Let's record that.
                            hiddenColumns.push(index);
                        }
                    });

                    // columns not being replaced must be cleared from input toggle-locks
                    this._unlockCells(this.element.find(".ui-table-cell-hidden, " +
                        ".ui-table-cell-visible"));

                    // update columntoggles and cells
                    this._addToggles(this._menu, create);

                    // At this point all columns are visible, so uncheck the checkboxes that correspond to
                    // those columns we've found to be hidden
                    for (index = hiddenColumns.length - 1 ; index > -1 ; index--) {
                        headers.eq(hiddenColumns[index]).jqmData("input")
                            .prop("checked", false)
                            .checkboxradio("refresh")
                            .trigger("change");
                    }
                }
            },

            _setToggleState: function () {
                this._menu.find("input").each(function () {
                    var checkbox = $(this);

                    this.checked = checkbox.jqmData("cells").eq(0).css("display") === "table-cell";
                    checkbox.checkboxradio("refresh");
                });
            },

            _destroy: function () {
                this._super();
            }
        });

    })(jQuery);

    (function ($, undefined) {

        $.widget("mobile.table", $.mobile.table, {
            options: {
                mode: "reflow",
                classes: $.extend($.mobile.table.prototype.options.classes, {
                    reflowTable: "ui-table-reflow",
                    cellLabels: "ui-table-cell-label"
                })
            },

            _create: function () {
                this._super();

                // If it's not reflow mode, return here.
                if (this.options.mode !== "reflow") {
                    return;
                }

                if (!this.options.enhanced) {
                    this.element.addClass(this.options.classes.reflowTable);

                    this._updateReflow();
                }
            },

            rebuild: function () {
                this._super();

                if (this.options.mode === "reflow") {
                    this._refresh(false);
                }
            },

            _refresh: function (create) {
                this._super(create);
                if (!create && this.options.mode === "reflow") {
                    this._updateReflow();
                }
            },

            _updateReflow: function () {
                var table = this,
                    opts = this.options;

                // get headers in reverse order so that top-level headers are appended last
                $(table.allHeaders.get().reverse()).each(function () {
                    var cells = $(this).jqmData("cells"),
                        colstart = $.mobile.getAttribute(this, "colstart"),
                        hierarchyClass = cells.not(this).filter("thead th").length && " ui-table-cell-label-top",
                        contents = $(this).clone().contents(),
                        iteration, filter;

                    if (contents.length > 0) {

                        if (hierarchyClass) {
                            iteration = parseInt(this.getAttribute("colspan"), 10);
                            filter = "";

                            if (iteration) {
                                filter = "td:nth-child(" + iteration + "n + " + (colstart) + ")";
                            }

                            table._addLabels(cells.filter(filter),
                                opts.classes.cellLabels + hierarchyClass, contents);
                        } else {
                            table._addLabels(cells, opts.classes.cellLabels, contents);
                        }

                    }
                });
            },

            _addLabels: function (cells, label, contents) {
                if (contents.length === 1 && contents[0].nodeName.toLowerCase() === "abbr") {
                    contents = contents.eq(0).attr("title");
                }
                // .not fixes #6006
                cells
                    .not(":has(b." + label + ")")
                        .prepend($("<b class='" + label + "'></b>").append(contents));
            }
        });

    })(jQuery);

    /*!
     * jQuery UI Tabs fadf2b312a05040436451c64bbfaf4814bc62c56
     * http://jqueryui.com
     *
     * Copyright 2013 jQuery Foundation and other contributors
     * Released under the MIT license.
     * http://jquery.org/license
     *
     * http://api.jqueryui.com/tabs/
     *
     * Depends:
     *	jquery.ui.core.js
     *	jquery.ui.widget.js
     */
    (function ($, undefined) {

        var tabId = 0,
            rhash = /#.*$/;

        function getNextTabId() {
            return ++tabId;
        }

        function isLocal(anchor) {
            return anchor.hash.length > 1 &&
                decodeURIComponent(anchor.href.replace(rhash, "")) ===
                    decodeURIComponent(location.href.replace(rhash, ""));
        }

        $.widget("ui.tabs", {
            version: "fadf2b312a05040436451c64bbfaf4814bc62c56",
            delay: 300,
            options: {
                active: null,
                collapsible: false,
                event: "click",
                heightStyle: "content",
                hide: null,
                show: null,

                // callbacks
                activate: null,
                beforeActivate: null,
                beforeLoad: null,
                load: null
            },

            _create: function () {
                var that = this,
                    options = this.options;

                this.running = false;

                this.element
                    .addClass("ui-tabs ui-widget ui-widget-content ui-corner-all")
                    .toggleClass("ui-tabs-collapsible", options.collapsible)
                    // Prevent users from focusing disabled tabs via click
                    .delegate(".ui-tabs-nav > li", "mousedown" + this.eventNamespace, function (event) {
                        if ($(this).is(".ui-state-disabled")) {
                            event.preventDefault();
                        }
                    })
                    // support: IE <9
                    // Preventing the default action in mousedown doesn't prevent IE
                    // from focusing the element, so if the anchor gets focused, blur.
                    // We don't have to worry about focusing the previously focused
                    // element since clicking on a non-focusable element should focus
                    // the body anyway.
                    .delegate(".ui-tabs-anchor", "focus" + this.eventNamespace, function () {
                        if ($(this).closest("li").is(".ui-state-disabled")) {
                            this.blur();
                        }
                    });

                this._processTabs();
                options.active = this._initialActive();

                // Take disabling tabs via class attribute from HTML
                // into account and update option properly.
                if ($.isArray(options.disabled)) {
                    options.disabled = $.unique(options.disabled.concat(
                        $.map(this.tabs.filter(".ui-state-disabled"), function (li) {
                            return that.tabs.index(li);
                        })
                    )).sort();
                }

                // check for length avoids error when initializing empty list
                if (this.options.active !== false && this.anchors.length) {
                    this.active = this._findActive(options.active);
                } else {
                    this.active = $();
                }

                this._refresh();

                if (this.active.length) {
                    this.load(options.active);
                }
            },

            _initialActive: function () {
                var active = this.options.active,
                    collapsible = this.options.collapsible,
                    locationHash = location.hash.substring(1);

                if (active === null) {
                    // check the fragment identifier in the URL
                    if (locationHash) {
                        this.tabs.each(function (i, tab) {
                            if ($(tab).attr("aria-controls") === locationHash) {
                                active = i;
                                return false;
                            }
                        });
                    }

                    // check for a tab marked active via a class
                    if (active === null) {
                        active = this.tabs.index(this.tabs.filter(".ui-tabs-active"));
                    }

                    // no active tab, set to false
                    if (active === null || active === -1) {
                        active = this.tabs.length ? 0 : false;
                    }
                }

                // handle numbers: negative, out of range
                if (active !== false) {
                    active = this.tabs.index(this.tabs.eq(active));
                    if (active === -1) {
                        active = collapsible ? false : 0;
                    }
                }

                // don't allow collapsible: false and active: false
                if (!collapsible && active === false && this.anchors.length) {
                    active = 0;
                }

                return active;
            },

            _getCreateEventData: function () {
                return {
                    tab: this.active,
                    panel: !this.active.length ? $() : this._getPanelForTab(this.active)
                };
            },

            _tabKeydown: function (event) {
                var focusedTab = $(this.document[0].activeElement).closest("li"),
                    selectedIndex = this.tabs.index(focusedTab),
                    goingForward = true;

                if (this._handlePageNav(event)) {
                    return;
                }

                switch (event.keyCode) {
                    case $.ui.keyCode.RIGHT:
                    case $.ui.keyCode.DOWN:
                        selectedIndex++;
                        break;
                    case $.ui.keyCode.UP:
                    case $.ui.keyCode.LEFT:
                        goingForward = false;
                        selectedIndex--;
                        break;
                    case $.ui.keyCode.END:
                        selectedIndex = this.anchors.length - 1;
                        break;
                    case $.ui.keyCode.HOME:
                        selectedIndex = 0;
                        break;
                    case $.ui.keyCode.SPACE:
                        // Activate only, no collapsing
                        event.preventDefault();
                        clearTimeout(this.activating);
                        this._activate(selectedIndex);
                        return;
                    case $.ui.keyCode.ENTER:
                        // Toggle (cancel delayed activation, allow collapsing)
                        event.preventDefault();
                        clearTimeout(this.activating);
                        // Determine if we should collapse or activate
                        this._activate(selectedIndex === this.options.active ? false : selectedIndex);
                        return;
                    default:
                        return;
                }

                // Focus the appropriate tab, based on which key was pressed
                event.preventDefault();
                clearTimeout(this.activating);
                selectedIndex = this._focusNextTab(selectedIndex, goingForward);

                // Navigating with control key will prevent automatic activation
                if (!event.ctrlKey) {
                    // Update aria-selected immediately so that AT think the tab is already selected.
                    // Otherwise AT may confuse the user by stating that they need to activate the tab,
                    // but the tab will already be activated by the time the announcement finishes.
                    focusedTab.attr("aria-selected", "false");
                    this.tabs.eq(selectedIndex).attr("aria-selected", "true");

                    this.activating = this._delay(function () {
                        this.option("active", selectedIndex);
                    }, this.delay);
                }
            },

            _panelKeydown: function (event) {
                if (this._handlePageNav(event)) {
                    return;
                }

                // Ctrl+up moves focus to the current tab
                if (event.ctrlKey && event.keyCode === $.ui.keyCode.UP) {
                    event.preventDefault();
                    this.active.focus();
                }
            },

            // Alt+page up/down moves focus to the previous/next tab (and activates)
            _handlePageNav: function (event) {
                if (event.altKey && event.keyCode === $.ui.keyCode.PAGE_UP) {
                    this._activate(this._focusNextTab(this.options.active - 1, false));
                    return true;
                }
                if (event.altKey && event.keyCode === $.ui.keyCode.PAGE_DOWN) {
                    this._activate(this._focusNextTab(this.options.active + 1, true));
                    return true;
                }
            },

            _findNextTab: function (index, goingForward) {
                var lastTabIndex = this.tabs.length - 1;

                function constrain() {
                    if (index > lastTabIndex) {
                        index = 0;
                    }
                    if (index < 0) {
                        index = lastTabIndex;
                    }
                    return index;
                }

                while ($.inArray(constrain(), this.options.disabled) !== -1) {
                    index = goingForward ? index + 1 : index - 1;
                }

                return index;
            },

            _focusNextTab: function (index, goingForward) {
                index = this._findNextTab(index, goingForward);
                this.tabs.eq(index).focus();
                return index;
            },

            _setOption: function (key, value) {
                if (key === "active") {
                    // _activate() will handle invalid values and update this.options
                    this._activate(value);
                    return;
                }

                if (key === "disabled") {
                    // don't use the widget factory's disabled handling
                    this._setupDisabled(value);
                    return;
                }

                this._super(key, value);

                if (key === "collapsible") {
                    this.element.toggleClass("ui-tabs-collapsible", value);
                    // Setting collapsible: false while collapsed; open first panel
                    if (!value && this.options.active === false) {
                        this._activate(0);
                    }
                }

                if (key === "event") {
                    this._setupEvents(value);
                }

                if (key === "heightStyle") {
                    this._setupHeightStyle(value);
                }
            },

            _tabId: function (tab) {
                return tab.attr("aria-controls") || "ui-tabs-" + getNextTabId();
            },

            _sanitizeSelector: function (hash) {
                return hash ? hash.replace(/[!"$%&'()*+,.\/:;<=>?@\[\]\^`{|}~]/g, "\\$&") : "";
            },

            refresh: function () {
                var options = this.options,
                    lis = this.tablist.children(":has(a[href])");

                // get disabled tabs from class attribute from HTML
                // this will get converted to a boolean if needed in _refresh()
                options.disabled = $.map(lis.filter(".ui-state-disabled"), function (tab) {
                    return lis.index(tab);
                });

                this._processTabs();

                // was collapsed or no tabs
                if (options.active === false || !this.anchors.length) {
                    options.active = false;
                    this.active = $();
                    // was active, but active tab is gone
                } else if (this.active.length && !$.contains(this.tablist[0], this.active[0])) {
                    // all remaining tabs are disabled
                    if (this.tabs.length === options.disabled.length) {
                        options.active = false;
                        this.active = $();
                        // activate previous tab
                    } else {
                        this._activate(this._findNextTab(Math.max(0, options.active - 1), false));
                    }
                    // was active, active tab still exists
                } else {
                    // make sure active index is correct
                    options.active = this.tabs.index(this.active);
                }

                this._refresh();
            },

            _refresh: function () {
                this._setupDisabled(this.options.disabled);
                this._setupEvents(this.options.event);
                this._setupHeightStyle(this.options.heightStyle);

                this.tabs.not(this.active).attr({
                    "aria-selected": "false",
                    tabIndex: -1
                });
                this.panels.not(this._getPanelForTab(this.active))
                    .hide()
                    .attr({
                        "aria-expanded": "false",
                        "aria-hidden": "true"
                    });

                // Make sure one tab is in the tab order
                if (!this.active.length) {
                    this.tabs.eq(0).attr("tabIndex", 0);
                } else {
                    this.active
                        .addClass("ui-tabs-active ui-state-active")
                        .attr({
                            "aria-selected": "true",
                            tabIndex: 0
                        });
                    this._getPanelForTab(this.active)
                        .show()
                        .attr({
                            "aria-expanded": "true",
                            "aria-hidden": "false"
                        });
                }
            },

            _processTabs: function () {
                var that = this;

                this.tablist = this._getList()
                    .addClass("ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all")
                    .attr("role", "tablist");

                this.tabs = this.tablist.find("> li:has(a[href])")
                    .addClass("ui-state-default ui-corner-top")
                    .attr({
                        role: "tab",
                        tabIndex: -1
                    });

                this.anchors = this.tabs.map(function () {
                    return $("a", this)[0];
                })
                    .addClass("ui-tabs-anchor")
                    .attr({
                        role: "presentation",
                        tabIndex: -1
                    });

                this.panels = $();

                this.anchors.each(function (i, anchor) {
                    var selector, panel, panelId,
                        anchorId = $(anchor).uniqueId().attr("id"),
                        tab = $(anchor).closest("li"),
                        originalAriaControls = tab.attr("aria-controls");

                    // inline tab
                    if (isLocal(anchor)) {
                        selector = anchor.hash;
                        panel = that.element.find(that._sanitizeSelector(selector));
                        // remote tab
                    } else {
                        panelId = that._tabId(tab);
                        selector = "#" + panelId;
                        panel = that.element.find(selector);
                        if (!panel.length) {
                            panel = that._createPanel(panelId);
                            panel.insertAfter(that.panels[i - 1] || that.tablist);
                        }
                        panel.attr("aria-live", "polite");
                    }

                    if (panel.length) {
                        that.panels = that.panels.add(panel);
                    }
                    if (originalAriaControls) {
                        tab.data("ui-tabs-aria-controls", originalAriaControls);
                    }
                    tab.attr({
                        "aria-controls": selector.substring(1),
                        "aria-labelledby": anchorId
                    });
                    panel.attr("aria-labelledby", anchorId);
                });

                this.panels
                    .addClass("ui-tabs-panel ui-widget-content ui-corner-bottom")
                    .attr("role", "tabpanel");
            },

            // allow overriding how to find the list for rare usage scenarios (#7715)
            _getList: function () {
                return this.element.find("ol,ul").eq(0);
            },

            _createPanel: function (id) {
                return $("<div>")
                    .attr("id", id)
                    .addClass("ui-tabs-panel ui-widget-content ui-corner-bottom")
                    .data("ui-tabs-destroy", true);
            },

            _setupDisabled: function (disabled) {
                if ($.isArray(disabled)) {
                    if (!disabled.length) {
                        disabled = false;
                    } else if (disabled.length === this.anchors.length) {
                        disabled = true;
                    }
                }

                // disable tabs
                for (var i = 0, li; (li = this.tabs[i]) ; i++) {
                    if (disabled === true || $.inArray(i, disabled) !== -1) {
                        $(li)
                            .addClass("ui-state-disabled")
                            .attr("aria-disabled", "true");
                    } else {
                        $(li)
                            .removeClass("ui-state-disabled")
                            .removeAttr("aria-disabled");
                    }
                }

                this.options.disabled = disabled;
            },

            _setupEvents: function (event) {
                var events = {
                    click: function (event) {
                        event.preventDefault();
                    }
                };
                if (event) {
                    $.each(event.split(" "), function (index, eventName) {
                        events[eventName] = "_eventHandler";
                    });
                }

                this._off(this.anchors.add(this.tabs).add(this.panels));
                this._on(this.anchors, events);
                this._on(this.tabs, { keydown: "_tabKeydown" });
                this._on(this.panels, { keydown: "_panelKeydown" });

                this._focusable(this.tabs);
                this._hoverable(this.tabs);
            },

            _setupHeightStyle: function (heightStyle) {
                var maxHeight,
                    parent = this.element.parent();

                if (heightStyle === "fill") {
                    maxHeight = parent.height();
                    maxHeight -= this.element.outerHeight() - this.element.height();

                    this.element.siblings(":visible").each(function () {
                        var elem = $(this),
                            position = elem.css("position");

                        if (position === "absolute" || position === "fixed") {
                            return;
                        }
                        maxHeight -= elem.outerHeight(true);
                    });

                    this.element.children().not(this.panels).each(function () {
                        maxHeight -= $(this).outerHeight(true);
                    });

                    this.panels.each(function () {
                        $(this).height(Math.max(0, maxHeight -
                            $(this).innerHeight() + $(this).height()));
                    })
                    .css("overflow", "auto");
                } else if (heightStyle === "auto") {
                    maxHeight = 0;
                    this.panels.each(function () {
                        maxHeight = Math.max(maxHeight, $(this).height("").height());
                    }).height(maxHeight);
                }
            },

            _eventHandler: function (event) {
                var options = this.options,
                    active = this.active,
                    anchor = $(event.currentTarget),
                    tab = anchor.closest("li"),
                    clickedIsActive = tab[0] === active[0],
                    collapsing = clickedIsActive && options.collapsible,
                    toShow = collapsing ? $() : this._getPanelForTab(tab),
                    toHide = !active.length ? $() : this._getPanelForTab(active),
                    eventData = {
                        oldTab: active,
                        oldPanel: toHide,
                        newTab: collapsing ? $() : tab,
                        newPanel: toShow
                    };

                event.preventDefault();

                if (tab.hasClass("ui-state-disabled") ||
                    // tab is already loading
                        tab.hasClass("ui-tabs-loading") ||
                    // can't switch durning an animation
                        this.running ||
                    // click on active header, but not collapsible
                        (clickedIsActive && !options.collapsible) ||
                    // allow canceling activation
                        (this._trigger("beforeActivate", event, eventData) === false)) {
                    return;
                }

                options.active = collapsing ? false : this.tabs.index(tab);

                this.active = clickedIsActive ? $() : tab;
                if (this.xhr) {
                    this.xhr.abort();
                }

                if (!toHide.length && !toShow.length) {
                    $.error("jQuery UI Tabs: Mismatching fragment identifier.");
                }

                if (toShow.length) {
                    this.load(this.tabs.index(tab), event);
                }
                this._toggle(event, eventData);
            },

            // handles show/hide for selecting tabs
            _toggle: function (event, eventData) {
                var that = this,
                    toShow = eventData.newPanel,
                    toHide = eventData.oldPanel;

                this.running = true;

                function complete() {
                    that.running = false;
                    that._trigger("activate", event, eventData);
                }

                function show() {
                    eventData.newTab.closest("li").addClass("ui-tabs-active ui-state-active");

                    if (toShow.length && that.options.show) {
                        that._show(toShow, that.options.show, complete);
                    } else {
                        toShow.show();
                        complete();
                    }
                }

                // start out by hiding, then showing, then completing
                if (toHide.length && this.options.hide) {
                    this._hide(toHide, this.options.hide, function () {
                        eventData.oldTab.closest("li").removeClass("ui-tabs-active ui-state-active");
                        show();
                    });
                } else {
                    eventData.oldTab.closest("li").removeClass("ui-tabs-active ui-state-active");
                    toHide.hide();
                    show();
                }

                toHide.attr({
                    "aria-expanded": "false",
                    "aria-hidden": "true"
                });
                eventData.oldTab.attr("aria-selected", "false");
                // If we're switching tabs, remove the old tab from the tab order.
                // If we're opening from collapsed state, remove the previous tab from the tab order.
                // If we're collapsing, then keep the collapsing tab in the tab order.
                if (toShow.length && toHide.length) {
                    eventData.oldTab.attr("tabIndex", -1);
                } else if (toShow.length) {
                    this.tabs.filter(function () {
                        return $(this).attr("tabIndex") === 0;
                    })
                    .attr("tabIndex", -1);
                }

                toShow.attr({
                    "aria-expanded": "true",
                    "aria-hidden": "false"
                });
                eventData.newTab.attr({
                    "aria-selected": "true",
                    tabIndex: 0
                });
            },

            _activate: function (index) {
                var anchor,
                    active = this._findActive(index);

                // trying to activate the already active panel
                if (active[0] === this.active[0]) {
                    return;
                }

                // trying to collapse, simulate a click on the current active header
                if (!active.length) {
                    active = this.active;
                }

                anchor = active.find(".ui-tabs-anchor")[0];
                this._eventHandler({
                    target: anchor,
                    currentTarget: anchor,
                    preventDefault: $.noop
                });
            },

            _findActive: function (index) {
                return index === false ? $() : this.tabs.eq(index);
            },

            _getIndex: function (index) {
                // meta-function to give users option to provide a href string instead of a numerical index.
                if (typeof index === "string") {
                    index = this.anchors.index(this.anchors.filter("[href$='" + index + "']"));
                }

                return index;
            },

            _destroy: function () {
                if (this.xhr) {
                    this.xhr.abort();
                }

                this.element.removeClass("ui-tabs ui-widget ui-widget-content ui-corner-all ui-tabs-collapsible");

                this.tablist
                    .removeClass("ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all")
                    .removeAttr("role");

                this.anchors
                    .removeClass("ui-tabs-anchor")
                    .removeAttr("role")
                    .removeAttr("tabIndex")
                    .removeUniqueId();

                this.tabs.add(this.panels).each(function () {
                    if ($.data(this, "ui-tabs-destroy")) {
                        $(this).remove();
                    } else {
                        $(this)
                            .removeClass("ui-state-default ui-state-active ui-state-disabled " +
                                "ui-corner-top ui-corner-bottom ui-widget-content ui-tabs-active ui-tabs-panel")
                            .removeAttr("tabIndex")
                            .removeAttr("aria-live")
                            .removeAttr("aria-busy")
                            .removeAttr("aria-selected")
                            .removeAttr("aria-labelledby")
                            .removeAttr("aria-hidden")
                            .removeAttr("aria-expanded")
                            .removeAttr("role");
                    }
                });

                this.tabs.each(function () {
                    var li = $(this),
                        prev = li.data("ui-tabs-aria-controls");
                    if (prev) {
                        li
                            .attr("aria-controls", prev)
                            .removeData("ui-tabs-aria-controls");
                    } else {
                        li.removeAttr("aria-controls");
                    }
                });

                this.panels.show();

                if (this.options.heightStyle !== "content") {
                    this.panels.css("height", "");
                }
            },

            enable: function (index) {
                var disabled = this.options.disabled;
                if (disabled === false) {
                    return;
                }

                if (index === undefined) {
                    disabled = false;
                } else {
                    index = this._getIndex(index);
                    if ($.isArray(disabled)) {
                        disabled = $.map(disabled, function (num) {
                            return num !== index ? num : null;
                        });
                    } else {
                        disabled = $.map(this.tabs, function (li, num) {
                            return num !== index ? num : null;
                        });
                    }
                }
                this._setupDisabled(disabled);
            },

            disable: function (index) {
                var disabled = this.options.disabled;
                if (disabled === true) {
                    return;
                }

                if (index === undefined) {
                    disabled = true;
                } else {
                    index = this._getIndex(index);
                    if ($.inArray(index, disabled) !== -1) {
                        return;
                    }
                    if ($.isArray(disabled)) {
                        disabled = $.merge([index], disabled).sort();
                    } else {
                        disabled = [index];
                    }
                }
                this._setupDisabled(disabled);
            },

            load: function (index, event) {
                index = this._getIndex(index);
                var that = this,
                    tab = this.tabs.eq(index),
                    anchor = tab.find(".ui-tabs-anchor"),
                    panel = this._getPanelForTab(tab),
                    eventData = {
                        tab: tab,
                        panel: panel
                    };

                // not remote
                if (isLocal(anchor[0])) {
                    return;
                }

                this.xhr = $.ajax(this._ajaxSettings(anchor, event, eventData));

                // support: jQuery <1.8
                // jQuery <1.8 returns false if the request is canceled in beforeSend,
                // but as of 1.8, $.ajax() always returns a jqXHR object.
                if (this.xhr && this.xhr.statusText !== "canceled") {
                    tab.addClass("ui-tabs-loading");
                    panel.attr("aria-busy", "true");

                    this.xhr
                        .success(function (response) {
                            // support: jQuery <1.8
                            // http://bugs.jquery.com/ticket/11778
                            setTimeout(function () {
                                panel.html(response);
                                that._trigger("load", event, eventData);
                            }, 1);
                        })
                        .complete(function (jqXHR, status) {
                            // support: jQuery <1.8
                            // http://bugs.jquery.com/ticket/11778
                            setTimeout(function () {
                                if (status === "abort") {
                                    that.panels.stop(false, true);
                                }

                                tab.removeClass("ui-tabs-loading");
                                panel.removeAttr("aria-busy");

                                if (jqXHR === that.xhr) {
                                    delete that.xhr;
                                }
                            }, 1);
                        });
                }
            },

            _ajaxSettings: function (anchor, event, eventData) {
                var that = this;
                return {
                    url: anchor.attr("href"),
                    beforeSend: function (jqXHR, settings) {
                        return that._trigger("beforeLoad", event,
                            $.extend({ jqXHR: jqXHR, ajaxSettings: settings }, eventData));
                    }
                };
            },

            _getPanelForTab: function (tab) {
                var id = $(tab).attr("aria-controls");
                return this.element.find(this._sanitizeSelector("#" + id));
            }
        });

    })(jQuery);

    (function ($, undefined) {

    })(jQuery);


}));
